<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
		xmlns:xhtml="http://www.w3.org/1999/xhtml"
>

<channel>
	<title>zaru blog &#187; プログラミング_PHP</title>
	<atom:link href="http://zaru.tofu-kun.org/category/%e3%83%97%e3%83%ad%e3%82%b0%e3%83%a9%e3%83%9f%e3%83%b3%e3%82%b0_php/feed/" rel="self" type="application/rss+xml" />
	<link>http://zaru.tofu-kun.org</link>
	<description>Web系のこととかー。</description>
	<lastBuildDate>Fri, 18 Nov 2011 02:28:49 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/category/%e3%83%97%e3%83%ad%e3%82%b0%e3%83%a9%e3%83%9f%e3%83%b3%e3%82%b0_php/feed/" />
		<item>
		<title>改良 : PHPでのページング処理のサンプル。</title>
		<link>http://zaru.tofu-kun.org/2011/06/28/%e6%94%b9%e8%89%af-php%e3%81%a7%e3%81%ae%e3%83%9a%e3%83%bc%e3%82%b8%e3%83%b3%e3%82%b0%e5%87%a6%e7%90%86%e3%81%ae%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab%e3%80%82/</link>
		<comments>http://zaru.tofu-kun.org/2011/06/28/%e6%94%b9%e8%89%af-php%e3%81%a7%e3%81%ae%e3%83%9a%e3%83%bc%e3%82%b8%e3%83%b3%e3%82%b0%e5%87%a6%e7%90%86%e3%81%ae%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab%e3%80%82/#comments</comments>
		<pubDate>Tue, 28 Jun 2011 06:08:20 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=357</guid>
		<description><![CDATA[ページング…？ PHPでのページング処理のサンプル。「とりあえず9JP」で、紹介されていたPHPのページング処理を見たら、対象のログファイルを全て読み込んで、そこから必要なデータを出力していたので、軽いファイルの時はまだしも、ちょっとファイルサイズが大きくなったら厳しいと思ったので、片手間に改良してみました。 改良版 PHPでのページング処理のサンプル。で作られたソースを極力変えずに、必要な部分のみ改修してあります。ちょっとエラーもあったので、合わせて修正してあります。 ソース class TekitouPager { private $dataArr = array(); private $pageNum = null; private $maxPageNum = null; function __construct($dataFile, $maxRecodeNum,$pageNum = '') { $start = $pageNum * $maxRecodeNum; $end = $start + $maxRecodeNum; $file = new SplFileObject($dataFile); for($i=$start;$i&#60;$end;$i++){ $file-&#62;seek($i); $this-&#62;dataArr[] = $file-&#62;current(); } //ページ数を計算 $count = 0; foreach ($file as $line) { [...]]]></description>
			<content:encoded><![CDATA[<h3>ページング…？</h3>
<p><a href="http://9jp.info/archives/11154" target="_blank">PHPでのページング処理のサンプル。「とりあえず9JP」</a>で、紹介されていたPHPのページング処理を見たら、対象のログファイルを全て読み込んで、そこから必要なデータを出力していたので、軽いファイルの時はまだしも、ちょっとファイルサイズが大きくなったら厳しいと思ったので、片手間に改良してみました。</p>
<h3>改良版</h3>
<p><a href="http://9jp.info/archives/11154" target="_blank">PHPでのページング処理のサンプル。</a>で作られたソースを極力変えずに、必要な部分のみ改修してあります。ちょっとエラーもあったので、合わせて修正してあります。</p>
<h4>ソース</h4>
<pre class="brush: php; title: ;">
class TekitouPager {

    private $dataArr = array();
    private $pageNum = null;
    private $maxPageNum = null;

    function __construct($dataFile, $maxRecodeNum,$pageNum = '') {
        $start = $pageNum * $maxRecodeNum;
        $end = $start + $maxRecodeNum;
        $file = new SplFileObject($dataFile);
        for($i=$start;$i&lt;$end;$i++){
            $file-&gt;seek($i);
            $this-&gt;dataArr[] = $file-&gt;current();
        }
        //ページ数を計算
        $count = 0;
        foreach ($file as $line) {
            $count++;
        }
        $this-&gt;maxPageNum = ceil($count / $maxRecodeNum);
        $this-&gt;pageNum = intval($pageNum);
    }

    function getPageData() {
        //指定されたページの配列を返す
        return $this-&gt;dataArr;
    }

    function getNaviLink() {
        $naviLink = '';
        for ($i = 0; $i &lt; $this-&gt;maxPageNum ; $i++) {
            //現在のページと一致する場合、太字等の強調
            if ($i != $this-&gt;pageNum) {
                $naviLink .= sprintf('&lt;a href=&quot;%s?page=%d&quot;&gt;%d&lt;/a&gt;&amp;nbsp;', $_SERVER['SCRIPT_NAME'], $i, $i);
            }
            else {
                $naviLink .= sprintf('&lt;b&gt;&lt;a href=&quot;%s?page=%d&quot;&gt;%d&lt;/a&gt;&lt;/b&gt;&amp;nbsp;', $_SERVER['SCRIPT_NAME'], $i, $i);
            }
        }
        return $naviLink;
    }

    function getNextLink() {
        //次のページが存在する場合、次のページへのリンク
        if ($this-&gt;maxPageNum &gt; $this-&gt;pageNum + 1) {
            return sprintf('&lt;a href=&quot;%s?page=%d&quot;&gt;Next&lt;/a&gt;&amp;nbsp;', $_SERVER['SCRIPT_NAME'], $this-&gt;pageNum + 1);
        }

    }

    function getPrevLink() {
        //現在のページが0では無い場合、前のページへのリンク
        if ($this-&gt;pageNum != 0) {
            return sprintf('&lt;a href=&quot;%s?page=%d&quot;&gt;Prev&lt;/a&gt;&amp;nbsp;', $_SERVER['SCRIPT_NAME'], $this-&gt;pageNum - 1);
        }
    }

}

if(isset($_GET['page'])){
    $page = $_GET['page'];
}else{
    $page = 0;
}
$pager = new TekitouPager('data.dat', 5,$page);
var_dump($pager-&gt;getPageData());
echo $pager-&gt;getPrevLink() . $pager-&gt;getNaviLink() . $pager-&gt;getNextLink();
</pre>
<h4>2011/06/28 23:15 追記</h4>
<p style="color:#ff0000;">とりあえず9JPの@buffbuffratさんから、次ページリンクの処理がこのままだとできないと指摘をいただいたため、慌てて修正しました。ありがとうございます。</p>
<p>変更箇所は、ファイルが何行あるのか調べるという部分です。</p>
<pre class="brush: php; title: ;">
$file = new SplFileObject($dataFile);
//ページ数を計算
$count = 0;
foreach ($file as $line) {
    $count++;
}
</pre>
<p>こんな感じです。</p>
<h4>なにを変えたか</h4>
<p>ページング自体のロジックは変更していません。ファイルを読み込む方法を変更してあります。元のソースは file() で全てを読み込み、配列に格納していましたが1000行も超えてくるとメモリ消費も馬鹿になりません。</p>
<p>そこで、SplFileObjectを使って、ファイルポインタを移動して、必要な行のみを取得しています。これであれば、メモリも最低限でいけます。</p>
<p>さすがに数万とかになってくるとポインタ移動だけで、けっこうな負荷になりますが、それでも全部読み込むよりはましだと思います。</p>
<h4>実際にパフォーマンスを測ってみた</h4>
<p>abで簡単にパフォーマンスを測ってみました。</p>
<table>
<tr>
<th></th>
<th>file()</th>
<th>SplFileObject</th>
</tr>
<tr>
<th>20行</th>
<td>1733 [#/sec]</td>
<td>1653 [#/sec]</td>
</tr>
<tr>
<th>500行</th>
<td>1180 [#/sec]</td>
<td>1292 [#/sec]</td>
</tr>
<tr>
<th>1,000行</th>
<td>844 [#/sec]</td>
<td>1246 [#/sec]</td>
</tr>
<tr>
<th>20,000行</th>
<td>40 [#/sec]</td>
<td>490 [#/sec]</td>
</tr>
</table>
<p>ファイルサイズが大きくなるにつれてパフォーマンスの差も出てきますね。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=357&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/06/28/%e6%94%b9%e8%89%af-php%e3%81%a7%e3%81%ae%e3%83%9a%e3%83%bc%e3%82%b8%e3%83%b3%e3%82%b0%e5%87%a6%e7%90%86%e3%81%ae%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab%e3%80%82/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/06/28/%e6%94%b9%e8%89%af-php%e3%81%a7%e3%81%ae%e3%83%9a%e3%83%bc%e3%82%b8%e3%83%b3%e3%82%b0%e5%87%a6%e7%90%86%e3%81%ae%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab%e3%80%82/" />
	</item>
		<item>
		<title>CakePHPで、MySQLとPostgreSQL両方を使いつつ、さらに違う文字コードに対応する方法</title>
		<link>http://zaru.tofu-kun.org/2011/04/28/cakephp%e3%81%a7%e3%80%81mysql%e3%81%a8postgresql%e4%b8%a1%e6%96%b9%e3%82%92%e4%bd%bf%e3%81%84%e3%81%a4%e3%81%a4%e3%80%81%e3%81%95%e3%82%89%e3%81%ab%e9%81%95%e3%81%86%e6%96%87%e5%ad%97%e3%82%b3/</link>
		<comments>http://zaru.tofu-kun.org/2011/04/28/cakephp%e3%81%a7%e3%80%81mysql%e3%81%a8postgresql%e4%b8%a1%e6%96%b9%e3%82%92%e4%bd%bf%e3%81%84%e3%81%a4%e3%81%a4%e3%80%81%e3%81%95%e3%82%89%e3%81%ab%e9%81%95%e3%81%86%e6%96%87%e5%ad%97%e3%82%b3/#comments</comments>
		<pubDate>Thu, 28 Apr 2011 06:42:58 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=343</guid>
		<description><![CDATA[めったにないタイプですが、つい先日すでに存在しているデータベースを使用して新しいサイトを立ち上げるという案件がありました。しかもMySQLはUTF-8だけど、PostgreSQLはEUC-JP…。おぉう。というわけで、CakePHPでサクっと両方のデータベースに対応してみました。 モデルの作成 config/database.php class DATABASE_CONFIG { //MySQL_DB var $default = array( 'driver' =&#62; 'mysql', 'persistent' =&#62; false, 'host' =&#62; 'localhost', 'login' =&#62; 'id', 'password' =&#62; 'pass', 'database' =&#62; 'name', 'prefix' =&#62; '', ); //PostgreSQL_DB var $pg = array( 'driver' =&#62; 'postgres', 'persistent' =&#62; false, 'host' =&#62; '192.168.0.100', 'login' =&#62; 'id', 'password' =&#62; 'pass', 'database' =&#62; [...]]]></description>
			<content:encoded><![CDATA[<p>めったにないタイプですが、つい先日すでに存在しているデータベースを使用して新しいサイトを立ち上げるという案件がありました。しかもMySQLはUTF-8だけど、PostgreSQLはEUC-JP…。おぉう。というわけで、CakePHPでサクっと両方のデータベースに対応してみました。</p>
<h3>モデルの作成</h3>
<h4>config/database.php</h4>
<pre class="brush: php; title: ;">
class DATABASE_CONFIG {

 //MySQL_DB
 var $default = array(
  'driver' =&gt; 'mysql',
  'persistent' =&gt; false,
  'host' =&gt; 'localhost',
  'login' =&gt; 'id',
  'password' =&gt; 'pass',
  'database' =&gt; 'name',
  'prefix' =&gt; '',
 );

 //PostgreSQL_DB
 var $pg = array(
  'driver' =&gt; 'postgres',
  'persistent' =&gt; false,
  'host' =&gt; '192.168.0.100',
  'login' =&gt; 'id',
  'password' =&gt; 'pass',
  'database' =&gt; 'name',
  'encoding' =&gt; 'EUC_JP',
  'prefix' =&gt; '',
  'port' =&gt; 5432,
 );
}
</pre>
<h4>app_model.php</h4>
<pre class="brush: php; title: ;">
//PostgreSQL用（EUC）
class PgAppModel extends Model {

 function beforeFind($queryData) {
  // クエリパラメータの配列を、DB文字コードに変換
  array_walk_recursive($queryData, array($this, 'encodeToDbEncoding'));
  return $queryData;
 }

 function afterFind($results, $primary = false) {
  // 取得結果の配列を、内部文字コードに変換
  array_walk_recursive($results, array($this, 'decodeFromDbEncoding'));
  return $results;
 }

 function beforeSave($options = array()) {
  // saveするデータの配列を、DB文字コードに変換
  array_walk_recursive($this-&gt;data, array($this, 'encodeToDbEncoding'));
  return true;
 }

 function query() {
  // Model::queryは可変の引数をとるので同じようオーバーライドにする
  $params = func_get_args();
  array_walk_recursive($params, array($this, 'encodeToDbEncoding'));
  $results = call_user_func_array(array('Model', 'query'), $params);
  if (is_array($results)) {
   $results = $this-&gt;afterFind($results);
  }
  return $results;
 }

 function encodeToDbEncoding(&amp;$value, $key) {
  if (is_string($value)) {
   $value = mb_convert_encoding($value, 'EUC-JP', Configure::read('App.encoding'));
  }
 }

 function decodeFromDbEncoding(&amp;$value, $key) {
  if (is_string($value)) {
   //機種依存文字をEUC-JP -&gt; UTF-8にすると文字化けするので、一旦 sjis-win に変換する
   //$value = mb_convert_encoding(mb_convert_encoding($value, 'sjis-win','EUC-JP'),Configure::read('App.encoding'), 'sjis-win');
   //でも、一部の機種依存のみの対応で大丈夫そうなので、 eucJP-win にする
   $value = mb_convert_encoding($value,Configure::read('App.encoding'), 'eucJP-win');
  }
 }
}

//MySQL用（UTF-8）
class AppModel extends Model {
}
</pre>
<p>ちょっと気をつけたいのは、EUC-JPからUTF-8に変換する際に、機種依存文字（「①」「㈱」「Ⅰ」など）が文字化けしてしまいます。いったん、sjis-winに変換してからUTF-8に変換をすれば文字化けをすることがありません。よく使われる文字の対応だけであれば、eucJP-winからUTF-8への変換でいけるようです。</p>
<h4>各モデル</h4>
<pre class="brush: php; title: ;">
/**
 * PostgreSQLを利用するモデル
 */
class Hoge extends PgAppModel{

 var $name = 'Hoge';
 var $useDbConfig = 'pg'; //PostgreSQLのデータベース情報に対応
 var $useTable = 'hoge'; //CakePHPの命名規則に従ってない場合、ここで指定
 var $primaryKey = 'cid'; //プライマリーキーも同様

}

/**
 * MySQLを利用するモデル
 */
class Fuga extends AppModel{

 var $name = 'Fuga';

}
</pre>
<h3>トランザクション</h3>
<p>違うデータベースを使用していた場合でも、トランザクションはできます。</p>
<pre class="brush: php; title: ;">
//それぞれのモデルをロード
App::import('Model','MyModel');
App::import('Model','PgModel');
$my = new MyModel;
$pg = new PgModel;

//MySQLトランザクション
$dataSourceMy = $my-&gt;getDataSource();
$dataSourceMy-&gt;begin($my);

//PostgreSQLトランザクション
$dataSourcePg = $pg-&gt;getDataSource();
$dataSourcePg-&gt;begin($pg);

//結果格納用
$return = array();

//登録処理
$return[] = $my-&gt;save($data);
$return[] = $pg-&gt;save($data);

//ロールバック・コミット
if(in_array(false,$return,true)) {
	$dataSourceMy-&gt;rollback($my);
	$dataSourcePg-&gt;rollback($pg);

	return false;
} else {
	$dataSourceMy-&gt;commit($my);
	$dataSourcePg-&gt;commit($pg);

	return true;
}
</pre>
<p>もっとスマートな方法があるのかもしれないですが、とりあえずこれで複数データベースに対応ができます。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=343&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/04/28/cakephp%e3%81%a7%e3%80%81mysql%e3%81%a8postgresql%e4%b8%a1%e6%96%b9%e3%82%92%e4%bd%bf%e3%81%84%e3%81%a4%e3%81%a4%e3%80%81%e3%81%95%e3%82%89%e3%81%ab%e9%81%95%e3%81%86%e6%96%87%e5%ad%97%e3%82%b3/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/04/28/cakephp%e3%81%a7%e3%80%81mysql%e3%81%a8postgresql%e4%b8%a1%e6%96%b9%e3%82%92%e4%bd%bf%e3%81%84%e3%81%a4%e3%81%a4%e3%80%81%e3%81%95%e3%82%89%e3%81%ab%e9%81%95%e3%81%86%e6%96%87%e5%ad%97%e3%82%b3/" />
	</item>
		<item>
		<title>CakePHP+PostgreSQLで、relation &#8216;hoge&#8217; does not existって怒られたときの対処法</title>
		<link>http://zaru.tofu-kun.org/2011/04/18/cakephppostgresql%e3%81%a7%e3%80%81relation-hoge-does-not-exist%e3%81%a3%e3%81%a6%e6%80%92%e3%82%89%e3%82%8c%e3%81%9f%e3%81%a8%e3%81%8d%e3%81%ae%e5%af%be%e5%87%a6%e6%b3%95/</link>
		<comments>http://zaru.tofu-kun.org/2011/04/18/cakephppostgresql%e3%81%a7%e3%80%81relation-hoge-does-not-exist%e3%81%a3%e3%81%a6%e6%80%92%e3%82%89%e3%82%8c%e3%81%9f%e3%81%a8%e3%81%8d%e3%81%ae%e5%af%be%e5%87%a6%e6%b3%95/#comments</comments>
		<pubDate>Mon, 18 Apr 2011 08:31:53 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[MySQL_PostgreSQL]]></category>
		<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=340</guid>
		<description><![CDATA[基本的に僕はCakePHP + MySQLの組み合わせで運用しているので、PostgreSQLでの運用経験がなく、基本的なところでつまずいてしまう。 今回は、データを save しようとしたら出たエラー「relation &#8216;hoge&#8217; does not exist」。実行しているSQLを確認すると…。 SELECT currval('hoge_did_sequence') as max あー、しまった。プライマリーキー用のシーケンス名が違うからか。というわけでモデルに var $sequence = '名前'; と設定してあげることで大丈夫です。]]></description>
			<content:encoded><![CDATA[<p>基本的に僕はCakePHP + MySQLの組み合わせで運用しているので、PostgreSQLでの運用経験がなく、基本的なところでつまずいてしまう。</p>
<p>今回は、データを save しようとしたら出たエラー「relation &#8216;hoge&#8217; does not exist」。実行しているSQLを確認すると…。</p>
<pre class="brush: sql; title: ;">SELECT currval('hoge_did_sequence') as max</pre>
<p>あー、しまった。プライマリーキー用のシーケンス名が違うからか。というわけでモデルに</p>
<pre class="brush: php; title: ;">var $sequence = '名前';</pre>
<p>と設定してあげることで大丈夫です。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=340&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/04/18/cakephppostgresql%e3%81%a7%e3%80%81relation-hoge-does-not-exist%e3%81%a3%e3%81%a6%e6%80%92%e3%82%89%e3%82%8c%e3%81%9f%e3%81%a8%e3%81%8d%e3%81%ae%e5%af%be%e5%87%a6%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/04/18/cakephppostgresql%e3%81%a7%e3%80%81relation-hoge-does-not-exist%e3%81%a3%e3%81%a6%e6%80%92%e3%82%89%e3%82%8c%e3%81%9f%e3%81%a8%e3%81%8d%e3%81%ae%e5%af%be%e5%87%a6%e6%b3%95/" />
	</item>
		<item>
		<title>ウェブル@soraiyさんのPHP で凄く簡単に GET/POST 送信ができる関数を勝手に改良した</title>
		<link>http://zaru.tofu-kun.org/2011/03/08/%e3%82%a6%e3%82%a7%e3%83%96%e3%83%absoraiy%e3%81%95%e3%82%93%e3%81%aephp-%e3%81%a7%e5%87%84%e3%81%8f%e7%b0%a1%e5%8d%98%e3%81%ab-getpost-%e9%80%81%e4%bf%a1%e3%81%8c%e3%81%a7%e3%81%8d%e3%82%8b/</link>
		<comments>http://zaru.tofu-kun.org/2011/03/08/%e3%82%a6%e3%82%a7%e3%83%96%e3%83%absoraiy%e3%81%95%e3%82%93%e3%81%aephp-%e3%81%a7%e5%87%84%e3%81%8f%e7%b0%a1%e5%8d%98%e3%81%ab-getpost-%e9%80%81%e4%bf%a1%e3%81%8c%e3%81%a7%e3%81%8d%e3%82%8b/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 08:55:16 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=320</guid>
		<description><![CDATA[soraiyさんが「PHP で凄く簡単に GET/POST 送信ができる関数を作りました」という記事でGET/POSTを簡単にできるものを公開していたんですが、ちょっと気になった部分があったので、勝手に改良してみました。 と思ったら、いろいろな人が既に反応していた…。でももう書いちゃったし、公開するか…。 var_dump(wbsRequest('GET', 'http://httpstat.us/200',array('key' =&#62; 'value'))); var_dump(wbsRequest2('GET', 'http://httpstat.us/200',array('key' =&#62; 'value'))); var_dump(wbsRequest('GET', 'http://httpstat.us/404')); var_dump(wbsRequest2('GET', 'http://httpstat.us/404')); //@soraiyさん作改良版 function wbsRequest($method, $url, $params = array()) { $data = http_build_query($params); $header = Array(&#34;Content-Type: application/x-www-form-urlencoded&#34;); $options = array('http' =&#62; Array( 'method' =&#62; $method, 'header' =&#62; implode(&#34;\r\n&#34;, $header), )); //ステータスをチェック / PHP5専用 get_headers() $respons = get_headers($url); if(preg_match(&#34;/(404&#124;403&#124;500)/&#34;,$respons['0'])){ return false; [...]]]></description>
			<content:encoded><![CDATA[<p>soraiyさんが「<a href="http://weble.org/2011/03/07/php-get-and-post-function">PHP で凄く簡単に GET/POST 送信ができる関数を作りました</a>」という記事でGET/POSTを簡単にできるものを公開していたんですが、ちょっと気になった部分があったので、勝手に改良してみました。</p>
<p>と思ったら、いろいろな人が既に反応していた…。でももう書いちゃったし、公開するか…。</p>
<pre class="brush: php; title: ;">
var_dump(wbsRequest('GET', 'http://httpstat.us/200',array('key' =&gt; 'value')));
var_dump(wbsRequest2('GET', 'http://httpstat.us/200',array('key' =&gt; 'value')));
var_dump(wbsRequest('GET', 'http://httpstat.us/404'));
var_dump(wbsRequest2('GET', 'http://httpstat.us/404'));

//@soraiyさん作改良版
function wbsRequest($method, $url, $params = array())
{
	$data = http_build_query($params);
	$header = Array(&quot;Content-Type: application/x-www-form-urlencoded&quot;);
	$options = array('http' =&gt; Array(
		'method' =&gt; $method,
		'header'  =&gt; implode(&quot;\r\n&quot;, $header),
	));

	//ステータスをチェック / PHP5専用 get_headers()
	$respons = get_headers($url);
	if(preg_match(&quot;/(404|403|500)/&quot;,$respons['0'])){
		return false;
	}

	if($method == 'GET') {
		$url = ($data != '')?$url.'?'.$data:$url;
	}elseif($method == 'POST') {
		$options['http']['content'] = $data;
	}
	$content = file_get_contents($url, false, stream_context_create($options));

	return $content;
}

//cURL版
function wbsRequest2($method, $url, $params = array())
{
	$data = http_build_query($params);
	if($method == 'GET') {
		$url = ($data != '')?$url.'?'.$data:$url;
	}

	$ch = curl_init($url);

	if($method == 'POST'){
		curl_setopt($ch,CURLOPT_POST,1);
		curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
	}

	//curl_setopt($ch, CURLOPT_HEADER,true); //header情報も一緒に欲しい場合
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	curl_setopt($ch, CURLOPT_TIMEOUT, 2);
	$res = curl_exec($ch);

	//ステータスをチェック
	$respons = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	if(preg_match(&quot;/^(404|403|500)$/&quot;,$respons)){
		return false;
	}

	return $res;
}
</pre>
<h4>cURL便利だよ！</h4>
<p>cURLは、細かくハンドリングできるし、なにより curl_multi_init が素晴らしいですね。並列で実行できます。</p>
<h4>file_get_contentsよりもcURLの方が高速だよ！</h4>
<p>1ラインで手軽に書ける file_get_contents は僕も重宝していますが、パフォーマンスにおいては、cURLの方が2倍弱ほど高速です。</p>
<h4>ステータスコードをチェック！</h4>
<p>リモートが 404 とか返してきたらエラーになっちゃうので、事前に get_headers とか使ってあげると良いかも。@でエラー抑止して$http_response_headerで取得してもいいと思います。</p>
<h4>httpstat.us便利！</h4>
<p>ステータスコードのチェックするときに、自前で用意しなくても <a href="http://httpstat.us/">http://httpstat.us/</a> ならURLの後ろにステータスコードを付け加えれば、そのコードで返してくれます。いつ使うんだ…と思っていたら、普通に便利でした。</p>
<h4>ちなみに</h4>
<p>POST時のパラメータ格納部分</p>
<pre class="brush: php; title: ;">
//$options['content'] = $data;
$options['http']['content'] = $data;
</pre>
<p>これが正しい…はず。</p>
<p>…と、勢いで作ってみたけど、間違っている箇所あるかも。校正プリーズ。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=320&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/03/08/%e3%82%a6%e3%82%a7%e3%83%96%e3%83%absoraiy%e3%81%95%e3%82%93%e3%81%aephp-%e3%81%a7%e5%87%84%e3%81%8f%e7%b0%a1%e5%8d%98%e3%81%ab-getpost-%e9%80%81%e4%bf%a1%e3%81%8c%e3%81%a7%e3%81%8d%e3%82%8b/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/03/08/%e3%82%a6%e3%82%a7%e3%83%96%e3%83%absoraiy%e3%81%95%e3%82%93%e3%81%aephp-%e3%81%a7%e5%87%84%e3%81%8f%e7%b0%a1%e5%8d%98%e3%81%ab-getpost-%e9%80%81%e4%bf%a1%e3%81%8c%e3%81%a7%e3%81%8d%e3%82%8b/" />
	</item>
		<item>
		<title>PHP Simple HTML DOM ParseでUA偽装をする</title>
		<link>http://zaru.tofu-kun.org/2011/02/21/php-simple-html-dom-parse%e3%81%a7ua%e5%81%bd%e8%a3%85%e3%82%92%e3%81%99%e3%82%8b/</link>
		<comments>http://zaru.tofu-kun.org/2011/02/21/php-simple-html-dom-parse%e3%81%a7ua%e5%81%bd%e8%a3%85%e3%82%92%e3%81%99%e3%82%8b/#comments</comments>
		<pubDate>Mon, 21 Feb 2011 09:58:38 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=308</guid>
		<description><![CDATA[PHPでHTMLをパースするで紹介したPHP Simple HTML DOM Parseで、UserAgentを偽装したくなったので、ちょこっとスクリプトを修正してみました。 simple_html_dom.php function file_get_html() { $dom = new simple_html_dom; $args = func_get_args(); $context = stream_context_create(array('http' =&#62; array( 'method' =&#62; 'GET', 'header' =&#62; 'User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)',//ユーザエージェント ))); $dom-&#62;load(call_user_func_array('file_get_contents', array($args['0'], false, $context)), true); return $dom; } [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://zaru.tofu-kun.org/2010/04/15/php%E3%81%A7html%E3%82%92%E3%83%91%E3%83%BC%E3%82%B9%E3%81%99%E3%82%8B/">PHPでHTMLをパースする</a>で紹介した<a href="http://simplehtmldom.sourceforge.net/">PHP Simple HTML DOM Parse</a>で、UserAgentを偽装したくなったので、ちょこっとスクリプトを修正してみました。</p>
<p>simple_html_dom.php</p>
<pre class="brush: php; title: ;">
function file_get_html() {
    $dom = new simple_html_dom;
    $args = func_get_args();
    $context = stream_context_create(array('http' =&gt; array(
        'method' =&gt; 'GET',
        'header' =&gt; 'User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)',//ユーザエージェント
    )));
    $dom-&gt;load(call_user_func_array('file_get_contents', array($args['0'], false, $context)), true);
    return $dom;
}
</pre>
<p>file_get_html() をまるっと置き換えればOK。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=308&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/02/21/php-simple-html-dom-parse%e3%81%a7ua%e5%81%bd%e8%a3%85%e3%82%92%e3%81%99%e3%82%8b/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/02/21/php-simple-html-dom-parse%e3%81%a7ua%e5%81%bd%e8%a3%85%e3%82%92%e3%81%99%e3%82%8b/" />
	</item>
		<item>
		<title>CakePHPで404ページをまとめて301転送する方法</title>
		<link>http://zaru.tofu-kun.org/2011/02/17/cakephp%e3%81%a7404%e3%83%9a%e3%83%bc%e3%82%b8%e3%82%92%e3%81%be%e3%81%a8%e3%82%81%e3%81%a6301%e8%bb%a2%e9%80%81%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95/</link>
		<comments>http://zaru.tofu-kun.org/2011/02/17/cakephp%e3%81%a7404%e3%83%9a%e3%83%bc%e3%82%b8%e3%82%92%e3%81%be%e3%81%a8%e3%82%81%e3%81%a6301%e8%bb%a2%e9%80%81%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 01:48:24 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[プログラミング_PHP]]></category>
		<category><![CDATA[技術その他]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=302</guid>
		<description><![CDATA[ちょっとした自分用メモレベルで失礼。 HTTPステータスコードが404 Not Foundのページを、301 Moved Permanentlyでトップページにリダイレクトをかける方法は、下記のようにmod_rewriteを使えば簡単にできるのだけど、CakePHPの場合はwebrootディレクトリ直下の.htaccessで、同様のことをindex.phpにパラメータを渡して、例のURLを実現しているので使えない。 シンプルなやり方 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ / [R=301,L] CakePHP用 appディレクトリ直下に error.php を作成し、404の時にheader()で301リダイレクトをかけるだけ。 class AppError extends ErrorHandler { function error404() { header(&#34;HTTP/1.1 301 Moved Permanently&#34;); header(&#34;Location: http://www.example.com/&#34;); } } 本当は、素直に404 Not Found専用のページを作るのが良いんだけど、案件によって必要だったので。]]></description>
			<content:encoded><![CDATA[<p>ちょっとした自分用メモレベルで失礼。</p>
<p>HTTPステータスコードが404 Not Foundのページを、301 Moved Permanentlyでトップページにリダイレクトをかける方法は、下記のようにmod_rewriteを使えば簡単にできるのだけど、CakePHPの場合はwebrootディレクトリ直下の.htaccessで、同様のことをindex.phpにパラメータを渡して、例のURLを実現しているので使えない。</p>
<h4>シンプルなやり方</h4>
<pre class="brush: bash; title: ;">
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ / [R=301,L]
</pre>
<h4>CakePHP用</h4>
<p>appディレクトリ直下に error.php を作成し、404の時にheader()で301リダイレクトをかけるだけ。</p>
<pre class="brush: php; title: ;">
class AppError extends ErrorHandler {

	function error404() {
		header(&quot;HTTP/1.1 301 Moved Permanently&quot;);
		header(&quot;Location: http://www.example.com/&quot;);
	}

}
</pre>
<p>本当は、素直に404 Not Found専用のページを作るのが良いんだけど、案件によって必要だったので。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=302&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/02/17/cakephp%e3%81%a7404%e3%83%9a%e3%83%bc%e3%82%b8%e3%82%92%e3%81%be%e3%81%a8%e3%82%81%e3%81%a6301%e8%bb%a2%e9%80%81%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/02/17/cakephp%e3%81%a7404%e3%83%9a%e3%83%bc%e3%82%b8%e3%82%92%e3%81%be%e3%81%a8%e3%82%81%e3%81%a6301%e8%bb%a2%e9%80%81%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95/" />
	</item>
		<item>
		<title>PHPで便利なソート色々まとめ</title>
		<link>http://zaru.tofu-kun.org/2011/01/18/php%e3%81%a7%e4%be%bf%e5%88%a9%e3%81%aa%e3%82%bd%e3%83%bc%e3%83%88%e8%89%b2%e3%80%85%e3%81%be%e3%81%a8%e3%82%81/</link>
		<comments>http://zaru.tofu-kun.org/2011/01/18/php%e3%81%a7%e4%be%bf%e5%88%a9%e3%81%aa%e3%82%bd%e3%83%bc%e3%83%88%e8%89%b2%e3%80%85%e3%81%be%e3%81%a8%e3%82%81/#comments</comments>
		<pubDate>Tue, 18 Jan 2011 07:29:49 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=284</guid>
		<description><![CDATA[多次元配列の中のキーでソート $lists = array( array( 'id' =&#62; 1, 'name' =&#62; 'a', ), array( 'id' =&#62; 2, 'name' =&#62; 'b', ), array( 'id' =&#62; 3, 'name' =&#62; 'c', ), array( 'id' =&#62; 4, 'name' =&#62; 'd', ), ); 多次元配列で、idをキーに降順でソート usort($lists, create_function('$a,$b','return($b[\'id\'] - $a[\'id\']);')); 多次元配列で、nameをキーに昇順でソート usort($lists, create_function('$a,$b','return($a[\'name\'] - $b[\'name\']);')); usort()を使えばユーザ定義比較関数を使うことができるので、階層の深い多次元配列でもソート可能。 array_multisort()を使う array_multisort() は本当に便利な関数で、上記と同じことをができる。 $keys = array(); foreach [...]]]></description>
			<content:encoded><![CDATA[<h3>多次元配列の中のキーでソート</h3>
<pre class="brush: php; title: ;">
$lists = array(
	array(
		'id' =&gt; 1,
		'name' =&gt; 'a',
	),
	array(
		'id' =&gt; 2,
		'name' =&gt; 'b',
	),
	array(
		'id' =&gt; 3,
		'name' =&gt; 'c',
	),
	array(
		'id' =&gt; 4,
		'name' =&gt; 'd',
	),
);
</pre>
<h4>多次元配列で、idをキーに降順でソート</h4>
<pre class="brush: php; title: ;">
usort($lists, create_function('$a,$b','return($b[\'id\'] - $a[\'id\']);'));
</pre>
<h4>多次元配列で、nameをキーに昇順でソート</h4>
<pre class="brush: php; title: ;">
usort($lists, create_function('$a,$b','return($a[\'name\'] - $b[\'name\']);'));
</pre>
<p>usort()を使えばユーザ定義比較関数を使うことができるので、階層の深い多次元配列でもソート可能。</p>
<h4>array_multisort()を使う</h4>
<p>array_multisort() は本当に便利な関数で、上記と同じことをができる。</p>
<pre class="brush: php; title: ;">
$keys = array();
foreach ($lists as $val) $keys[] = $val['name'];
array_multisort($keys, SORT_DESC, $lists);
</pre>
<h4>usort() と array_multisort() の実行速度を比較</h4>
<p>apacheのabでパフォーマンスを比較したところ、僕の環境下では array_multisort() の方が、usort() よりも5倍早かった。</p>
<h3>連想インデックスを保持しつつソート</h3>
<pre class="brush: php; title: ;">
$lists = array(
	'a' =&gt; array(
		'id' =&gt; 1,
		'name' =&gt; 'a',
	),
	'b' =&gt; array(
		'id' =&gt; 2,
		'name' =&gt; 'b',
	),
	'c' =&gt; array(
		'id' =&gt; 3,
		'name' =&gt; 'c',
	),
	'd' =&gt; array(
		'id' =&gt; 4,
		'name' =&gt; 'd',
	),
);
</pre>
<h4>連想配列で、idをキーに降順でソート（連想インデックスを保持）</h4>
<pre class="brush: php; title: ;">
uasort($lists, create_function('$a,$b','return($b[\'id\'] - $a[\'id\']);'));
</pre>
<h3>連想インデックスでソート</h3>
<pre class="brush: php; title: ;">
$lists = array(
	'safari' =&gt; array(
		'id' =&gt; 1,
	),
	'ie' =&gt; array(
		'id' =&gt; 2,
	),
	'firefox' =&gt; array(
		'id' =&gt; 3,
	),
	'chrome' =&gt; array(
		'id' =&gt; 4,
	),
	'opera' =&gt; array(
		'id' =&gt; 5,
	),
);
</pre>
<h4>（多次元）配列で、連想インデックスを降順でソート（連想インデックスを保持）</h4>
<pre class="brush: php; title: ;">
krsort($lists);
//ksort()が昇順
</pre>
<p>下記の uksort() を使えば、この場合は krsort() と同じ結果に。</p>
<pre class="brush: php; title: ;">
uksort($lists, create_function('$a,$b','return(strcasecmp($b,$a));'));
</pre>
<p>uksort() を使うメリットとしては、やはりユーザ定義比較関数が使えるので、PHPマニュアルにあるように下記のようなことができる。</p>
<pre class="brush: php; title: ;">
function cmp($a, $b){
	$a = preg_replace('@^(a|an|the) @', '', $a);
	$b = preg_replace('@^(a|an|the) @', '', $b);
	return strcasecmp($a, $b);
}

$a = array(&quot;John&quot; =&gt; 1, &quot;the Earth&quot; =&gt; 2, &quot;an apple&quot; =&gt; 3, &quot;a banana&quot; =&gt; 4);

uksort($a, &quot;cmp&quot;);
</pre>
<h3>日付・時刻でソート</h3>
<pre class="brush: php; title: ;">
$lists = array(
	array(
		'id' =&gt; 1,
		'date' =&gt; '2011-01-01 12:00:00',
	),
	array(
		'id' =&gt; 2,
		'date' =&gt; '2010-12-01 12:00:00',
	),
	array(
		'id' =&gt; 3,
		'date' =&gt; '2011-02-01 12:00:00',
	),
	array(
		'id' =&gt; 4,
		'date' =&gt; '2009-01-01 12:00:00',
	),
);
</pre>
<p>strtotime() でタイムスタンプに変換して比較することもできる。便利。</p>
<pre class="brush: php; title: ;">
usort($lists, create_function('$a,$b','return(strtotime($a[\'date\']) &gt; strtotime($b[\'date\']));'));
</pre>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=284&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/01/18/php%e3%81%a7%e4%be%bf%e5%88%a9%e3%81%aa%e3%82%bd%e3%83%bc%e3%83%88%e8%89%b2%e3%80%85%e3%81%be%e3%81%a8%e3%82%81/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/01/18/php%e3%81%a7%e4%be%bf%e5%88%a9%e3%81%aa%e3%82%bd%e3%83%bc%e3%83%88%e8%89%b2%e3%80%85%e3%81%be%e3%81%a8%e3%82%81/" />
	</item>
		<item>
		<title>CakePHP ホスト名でviewsのディレクトリを切り替える</title>
		<link>http://zaru.tofu-kun.org/2011/01/07/cakephp-%e3%83%9b%e3%82%b9%e3%83%88%e5%90%8d%e3%81%a7views%e3%81%ae%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%e3%82%92%e5%88%87%e3%82%8a%e6%9b%bf%e3%81%88%e3%82%8b/</link>
		<comments>http://zaru.tofu-kun.org/2011/01/07/cakephp-%e3%83%9b%e3%82%b9%e3%83%88%e5%90%8d%e3%81%a7views%e3%81%ae%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%e3%82%92%e5%88%87%e3%82%8a%e6%9b%bf%e3%81%88%e3%82%8b/#comments</comments>
		<pubDate>Fri, 07 Jan 2011 10:56:01 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=280</guid>
		<description><![CDATA[Virtual Hostで複数のドメインを一つのサーバで、しかもCakePHPも使い回したい。ということで、viewの（viewだけじゃなくcontrollerやmodelも）ディレクトリ構成をホスト名毎に切り替える方法。実に簡単なマニュアルレベル。 /app/config/bootstrap.php App::build(array( 'views' =&#62; array( ROOT.DS.APP_DIR.DS.'views'.DS.$_SERVER['HTTP_HOST'].DS, ROOT.DS.APP_DIR.DS.'views'.DS, ), )); こうすれば /app/views/www.example-a.com/ /app/views/www.example-b.com/ で、それぞれ使い分けることが可能。]]></description>
			<content:encoded><![CDATA[<p>Virtual Hostで複数のドメインを一つのサーバで、しかもCakePHPも使い回したい。ということで、viewの（viewだけじゃなくcontrollerやmodelも）ディレクトリ構成をホスト名毎に切り替える方法。実に簡単なマニュアルレベル。</p>
<h4>/app/config/bootstrap.php</h4>
<pre class="brush: php; title: ;">
App::build(array(
    'views' =&gt; array(
        ROOT.DS.APP_DIR.DS.'views'.DS.$_SERVER['HTTP_HOST'].DS,
        ROOT.DS.APP_DIR.DS.'views'.DS,
    ),
));
</pre>
<p>こうすれば</p>
<p>/app/views/www.example-a.com/<br />
/app/views/www.example-b.com/</p>
<p>で、それぞれ使い分けることが可能。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=280&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2011/01/07/cakephp-%e3%83%9b%e3%82%b9%e3%83%88%e5%90%8d%e3%81%a7views%e3%81%ae%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%e3%82%92%e5%88%87%e3%82%8a%e6%9b%bf%e3%81%88%e3%82%8b/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2011/01/07/cakephp-%e3%83%9b%e3%82%b9%e3%83%88%e5%90%8d%e3%81%a7views%e3%81%ae%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%e3%82%92%e5%88%87%e3%82%8a%e6%9b%bf%e3%81%88%e3%82%8b/" />
	</item>
		<item>
		<title>PHPをtopできるphptopを動かしてみた</title>
		<link>http://zaru.tofu-kun.org/2010/12/21/php%e3%82%92top%e3%81%a7%e3%81%8d%e3%82%8bphptop%e3%82%92%e5%8b%95%e3%81%8b%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/</link>
		<comments>http://zaru.tofu-kun.org/2010/12/21/php%e3%82%92top%e3%81%a7%e3%81%8d%e3%82%8bphptop%e3%82%92%e5%8b%95%e3%81%8b%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/#comments</comments>
		<pubDate>Tue, 21 Dec 2010 05:25:43 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[サーバ_Linux_Windows]]></category>
		<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=265</guid>
		<description><![CDATA[phptop = どのPHPに時間や負荷がかかっているか見られる ちょっと前に少し話題になったPHPをスクリプト毎（URIベース）で、topコマンド風に状況が見られるphptopを動かしてみた。 必要な環境 PHP5.2.0以上 Perl CPAN Term::Size phptopのダウンロード phptopのwikiにはphptop requires at least PHP >= 4.3.10 or PHP >= 5.0.3.って書いてあるけど、PHP5.2からの関数 memory_get_peak_usage() を使ってメモリ使用量を取得しているので、PHP5.2.0以上は必要。と思う。というか、5.1以下ではエラーで動かなかった。 なお、Term::Sizeモジュールを使用しているので、CPANからインストールしておくこと。 設定 /etc/php.ini log_errors=on auto_prepend_file=/usr/local/src/phptop_hook.php /usr/local/src/phptop #118行目あたり・単純にコメントアウトしても動くみたい #setlocale(POSIX::LC_NUMERIC, 'C'); # Use . as decimal separator setlocale(LC_NUMERIC, 'C'); # Use . as decimal separator #355行目あたり #push(@log, '/var/log/apache2/error*log', '/var/log/apache2/*/error*log') if !@log; push(@log, '/var/log/httpd/*error_log', '/var/log/httpd/*/error_log') [...]]]></description>
			<content:encoded><![CDATA[<h3>phptop = どのPHPに時間や負荷がかかっているか見られる</h3>
<p>ちょっと前に少し話題になったPHPをスクリプト毎（URIベース）で、topコマンド風に状況が見られるphptopを動かしてみた。</p>
<h4>必要な環境</h4>
<ul>
<li>PHP5.2.0以上</li>
<li>Perl</li>
<li>CPAN Term::Size</li>
<li><a href="http://forge.bearstech.com/trac/wiki/PhpTop" target="_blank">phptopのダウンロード</a></li>
</ul>
<p>phptopのwikiには<q>phptop requires at least PHP >= 4.3.10 or PHP >= 5.0.3.</q>って書いてあるけど、PHP5.2からの関数 <a href="http://php.net/manual/ja/function.memory-get-peak-usage.php" target="_blank">memory_get_peak_usage()</a> を使ってメモリ使用量を取得しているので、PHP5.2.0以上は必要。と思う。というか、5.1以下ではエラーで動かなかった。</p>
<p>なお、Term::Sizeモジュールを使用しているので、CPANからインストールしておくこと。</p>
<h4>設定</h4>
<p>/etc/php.ini</p>
<pre class="brush: bash; title: ;">
log_errors=on
auto_prepend_file=/usr/local/src/phptop_hook.php
</pre>
<p>/usr/local/src/phptop</p>
<pre class="brush: perl; title: ;">
#118行目あたり・単純にコメントアウトしても動くみたい
#setlocale(POSIX::LC_NUMERIC, 'C'); # Use . as decimal separator
setlocale(LC_NUMERIC, 'C'); # Use . as decimal separator

#355行目あたり
#push(@log, '/var/log/apache2/error*log', '/var/log/apache2/*/error*log') if !@log;
push(@log, '/var/log/httpd/*error_log', '/var/log/httpd/*/error_log') if !@log;
</pre>
<p>Apacheのエラーログの場所は適宜変更。Apacheエラーログに、phptop_hook.phpを通じてデータを吐き出す仕様になっている。</p>
<h4>使用方法</h4>
<p>php.iniを変更したので、Apacheを再起動後、PHPスクリプトへアクセスをする。Apacheのエラーログにデータが書き出されているのを確認したら、下記コマンドにてデータが出力される。</p>
<pre class="brush: bash; title: ;">
$./phptop -t 15 -s mem
</pre>
<p>No phptop records found. と出たら、正常にデータが取れていないので、エラーログを確認したり、php.iniが反映されているか確認する。</p>
<p>これを実践的に使う機会は、あまりないと思うけど（誰かが作った謎なシステムを別の側面から把握するためとか？）なかなか面白いなーと思った。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=265&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2010/12/21/php%e3%82%92top%e3%81%a7%e3%81%8d%e3%82%8bphptop%e3%82%92%e5%8b%95%e3%81%8b%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2010/12/21/php%e3%82%92top%e3%81%a7%e3%81%8d%e3%82%8bphptop%e3%82%92%e5%8b%95%e3%81%8b%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/" />
	</item>
		<item>
		<title>PHP5で、マルチアクセスな似非クローラ作った</title>
		<link>http://zaru.tofu-kun.org/2010/12/10/php5%e3%81%a7%e3%80%81%e4%bc%bc%e9%9d%9e%e3%83%9e%e3%83%ab%e3%83%81%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%81%aa%e3%82%af%e3%83%ad%e3%83%bc%e3%83%a9%e4%bd%9c%e3%81%a3%e3%81%9f/</link>
		<comments>http://zaru.tofu-kun.org/2010/12/10/php5%e3%81%a7%e3%80%81%e4%bc%bc%e9%9d%9e%e3%83%9e%e3%83%ab%e3%83%81%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%81%aa%e3%82%af%e3%83%ad%e3%83%bc%e3%83%a9%e4%bd%9c%e3%81%a3%e3%81%9f/#comments</comments>
		<pubDate>Fri, 10 Dec 2010 06:03:31 +0000</pubDate>
		<dc:creator>zaru</dc:creator>
				<category><![CDATA[プログラミング_PHP]]></category>

		<guid isPermaLink="false">http://zaru.tofu-kun.org/?p=258</guid>
		<description><![CDATA[使い捨てレベルで、クローラを作らなくちゃならなくなったので、とりあえず file_get_contents でもループして放置するか…とも思ったけれど、せっかくなので同時に4接続ほどするようにしてみた。PHP5には curl_multi_exec という便利なものがあるので、これを使う。 ソース クローラクラス &#60;?php class multiCrawler{ var $uriArray = array(); var $output = array(); var $timeout = 0; var $ua = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'; //最大同時接続数 var $accessMaxNum = 4; var $start = 0; //エラーログを出力するかどうか var $errorLog = false; var $errorLogFile = 'crowl_error.txt'; //クロール対象URLをセット function [...]]]></description>
			<content:encoded><![CDATA[<p>使い捨てレベルで、クローラを作らなくちゃならなくなったので、とりあえず file_get_contents でもループして放置するか…とも思ったけれど、せっかくなので同時に4接続ほどするようにしてみた。PHP5には <a href="http://php.net/manual/ja/function.curl-multi-exec.php" target="_blank">curl_multi_exec</a> という便利なものがあるので、これを使う。</p>
<h3>ソース</h3>
<p>クローラクラス</p>
<pre class="brush: php; title: ;">
&lt;?php
class multiCrawler{

	var $uriArray = array();
	var $output = array();
	var $timeout = 0;
	var $ua = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';

	//最大同時接続数
	var $accessMaxNum = 4;
	var $start = 0;

	//エラーログを出力するかどうか
	var $errorLog = false;
	var $errorLogFile = 'crowl_error.txt';

	//クロール対象URLをセット
	function setUri($uri){
		$this-&gt;uriArray = (is_array($uri))?$uri:array($uri);
	}

	//クロールエラーログフラグをセット
	function setErrorLogFlg($bool){
		$this-&gt;errorLog = $bool;
	}

	//クロールエラーログファイルをセット
	function setErrorLogFile($file){
		$this-&gt;errorLogFile = $file;
	}

	//タイムアウトをセット
	function setTimeout($timeout){
		$this-&gt;timeout = (is_int($timeout))?$timeout:0;
	}

	//エラーを出力
	function outputError($uri,$error){
		$fp = fopen($this-&gt;errorLogFile,'a');
		fputs($fp,date('Y-m-d H:i:s') . ' ' . $uri . ' : ' . $error.&quot;\n&quot;);
		fclose($fp);
	}

	//実行
	function execute(){

		$this-&gt;output = array();

		$count = count($this-&gt;uriArray);
		$num = ceil($count / $this-&gt;accessMaxNum);
		for($i=0;$i&lt;$num;$i++){
			sleep(1);
			$this-&gt;start = $i * $this-&gt;accessMaxNum;
			$this-&gt;end = ($i + 1) * $this-&gt;accessMaxNum;
			$this-&gt;fetchUri();
		}

		return $this-&gt;output;
	}

	//クロール
	function fetchUri(){

		for($i=$this-&gt;start;$i&lt;$this-&gt;end;$i++){
			if(!isset($this-&gt;uriArray[$i]) || $this-&gt;uriArray[$i] == ''){
				$this-&gt;end = $i;
				break;
			}
		}

		for($i=$this-&gt;start;$i&lt;$this-&gt;end;$i++){

			$ch[$i] = curl_init();
			curl_setopt($ch[$i],CURLOPT_URL, trim($this-&gt;uriArray[$i]));
			curl_setopt($ch[$i],CURLOPT_HEADER, 0);
			curl_setopt($ch[$i],CURLOPT_RETURNTRANSFER,1);
			curl_setopt($ch[$i],CURLOPT_FAILONERROR,1);
			curl_setopt($ch[$i],CURLOPT_FOLLOWLOCATION,1);
			curl_setopt($ch[$i],CURLOPT_MAXREDIRS,3);
			curl_setopt($ch[$i],CURLOPT_SSL_VERIFYPEER,false);
			curl_setopt($ch[$i],CURLOPT_SSL_VERIFYHOST,false);
			curl_setopt($ch[$i],CURLOPT_USERAGENT,$this-&gt;ua);
			if($this-&gt;timeout){
				curl_setopt($ch[$i],CURLOPT_CONNECTTIMEOUT,$this-&gt;timeout);
				curl_setopt($ch[$i],CURLOPT_TIMEOUT,$this-&gt;timeout);
			}
		}

		$mh = curl_multi_init();

		for($i=$this-&gt;start;$i&lt;$this-&gt;end;$i++){
			curl_multi_add_handle($mh,$ch[$i]);
		}

		$active = null;
		do {
			$mrc = curl_multi_exec($mh, $active);
		} while ($mrc == CURLM_CALL_MULTI_PERFORM);

		while ($active &amp;&amp; $mrc == CURLM_OK) {
			if (curl_multi_select($mh) != -1) {
				do {
					$mrc = curl_multi_exec($mh, $active);
				} while ($mrc == CURLM_CALL_MULTI_PERFORM);
			}
		}

		for($i=$this-&gt;start;$i&lt;$this-&gt;end;$i++){
			$this-&gt;output[$i] = (curl_error($ch[$i]) == '')?curl_multi_getcontent($ch[$i]):false;
			if($this-&gt;output[$i] === false &amp;&amp; $this-&gt;errorLog){
				$this-&gt;outputError($this-&gt;uriArray[$i],curl_error($ch[$i]));
			}
			curl_multi_remove_handle($mh,$ch[$i]);
			curl_close($ch[$i]);
		}

		curl_multi_close($mh);

	}
}
</pre>
<p>使ってみる</p>
<pre class="brush: php; title: ;">
&lt;?php
	include('multiCrawler.php');
	$uriArray = array(
		'http://www.yahoo.co.jp/',
		'http://www.google.co.jp/',
	);

	$mc = new multiCrawler;
	$mc-&gt;setUri($uriArray);
	//$mc-&gt;setErrorLogFlg(1); //エラーログ
	//$mc-&gt;setTimeout(10); //タイムアウト設定
	$result = $mc-&gt;execute();

	print_r($result);
</pre>
<p>ソースを見てもらえれば、そんなに難しいことはやっていないので、カスタマイズも簡単。同時に4URLへ接続し（設定したURLが5つ以上の場合は、1秒間のスリープ）、データをごそっと配列へ入れて返す仕様。</p>
<p>つまり、設定したURLが膨大、もしくはアクセス先のファイルサイズが大きいと、それだけメモリ食う。使う際には、配列に格納じゃなくて、ファイルに書き出しとかすれば良いんじゃないかな。</p>
<p>今度は、RubyかPythonで、もっと柔軟なクローラでも作ってみますか。</p>
<img src="http://zaru.tofu-kun.org/?ak_action=api_record_view&id=258&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://zaru.tofu-kun.org/2010/12/10/php5%e3%81%a7%e3%80%81%e4%bc%bc%e9%9d%9e%e3%83%9e%e3%83%ab%e3%83%81%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%81%aa%e3%82%af%e3%83%ad%e3%83%bc%e3%83%a9%e4%bd%9c%e3%81%a3%e3%81%9f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://zaru.tofu-kun.org/2010/12/10/php5%e3%81%a7%e3%80%81%e4%bc%bc%e9%9d%9e%e3%83%9e%e3%83%ab%e3%83%81%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%81%aa%e3%82%af%e3%83%ad%e3%83%bc%e3%83%a9%e4%bd%9c%e3%81%a3%e3%81%9f/" />
	</item>
	</channel>
</rss>
