Ethnaでリダイレクトの件はどうなった?

Ethnaでリダイレクト(ロケーション)させるのっていまのところまだ決定版てでてないんでしょうか。

探したところ下のところが見つかったので、参考にさせていただきつつ僕も考えてみました。

Appid_ActionClass.php:

<?php

function redirect($place, $params = array())
{
    // 遷移場所を決定
    if (preg_match('/^https?:\/\//', $place)) {
        // URL
        $url = $place;
    } else {
        $url = $this->config->get('url');

        if (preg_match('/^\//', $place)) {
            // 絶対パス
            $url .= $place;
        } elseif (preg_match('/\//', $place)) {
            // 相対パス
            $url .= dirname($_SERVER['SCRIPT_NAME']).'/'.$place;
        } else {
            // アクション名
            $url .= $_SERVER['SCRIPT_NAME'].'?'.$this->backend->controller->getActionRequest($place, 'url');
        }
    }

    // リクエストパラメータを連結
    if (count($params) > 0) {
        if (strpos($url, '?') === false) {
            $url .= '?';
        } else {
            $url .= '&';
        }
        $url .= http_build_query($params);
    }

    return 'Location: '.$url;
}

Appid_Controller.php:

<?php

function _sortForward($action_name, $retval)
{
    $forward_name = $retval;
    if (preg_match('/^Location: (.*)$/i', $forward_name)) {
        $this->logger->log(LOG_INFO, 'forward_location[%s]', $forward_name);
        header($forward_name);
        return null;
    }

    $forward_path = $this->_getForwardPath($forward_name);
    $this->logger->log(LOG_INFO, 'forward_name[%s]', $forward_name);
    return $forward_name;
}

アクションにredirect()を追加し、コントローラで_sortForward()をオーバーライドしました。
使い方はこんな感じ。

<?php

    function perform()
    {
        // hogeアクションにリダイレクト
        return $this->redirect('hoge');

        /*
        - $this->config->get('url') が http://www.example.com/aaa
        - エントリポイントがindex.php
        の場合

        // アクション名
        return $this->redirect('hoge');
        // (Location: http://www.example.com/aaa/index.php?action_hoge=true)

        // URL
        return $this->redirect('http://www.google.co.jp/');
        // (Location: http://www.google.co.jp/)

        // 相対パス
        return $this->redirect('hoge/fuga');
        // (Location: http://www.example.com/aaa/hoge/fuga)

        // 絶対パス
        return $this->redirect('/hoge/fuga');
        // (Location: http://www.example.com/hoge/fuga)
        */
    }

しかしこの方法だと「ページを移動しています」みたいな画面を出せないのでいまいちかも。むーん。

Diggin_Scraperのフィルタに匿名関数を使いたい

Diggin_Scraperで自前のフィルタを用意する場合、2つの方法があります(たぶん)。

  1. 関数を定義する
  2. Zend_Filter_Interfaceを継承したクラスを定義する

でも例えばちょっとした置換をしたいためだけにクラスを定義するのは面倒だし、ライブラリやフレームワークの中で使うときに関数を定義するのはよろしくないので、現状は結果をforeachで回して一個ずつ処理してるんですが、なんともいけてないです。

これってフィルタに匿名関数使えたら解決しないかなーと思ったので、使えるようにする方法を考えてみました。

Diggin_Scraperでフィルタの呼び出し・実行をしてるのはDiggin_Scraper_Filter::run()で、ここでフィルタ名からどのフィルタを実行するかを決定してるもよう。

<?php
    public static function run($values, $filters, $filterParams = null)
    {
        foreach ($filters as $filter) {

            $return = array();

            if (preg_match('/^[0-9a-zA-Z]/', $filter)) {
                if (function_exists($filter)) {
                    foreach ($values as $value) {
                        $return[] = call_user_func($filter, $value);
                    }
                } elseif (!strstr($filter, '_')) {
                    require_once 'Zend/Filter.php';
                    foreach ($values as $value) {
                        $return[] = Zend_Filter::get($value, $filter);
                    }
                } else {
                    require_once 'Zend/Loader.php';
                    try {
                        Zend_Loader::loadClass($filter);
                    } catch (Zend_Exception $e) {
                        require_once 'Diggin/Scraper/Filter/Exception.php';
                        throw new Diggin_Scraper_Filter_Exception("Unable to load filter '$filter': {$e->getMessage()}");
                    }
                    $filter = new $filter();
                    foreach ($values as $value) {
                        $return[] = $filter->filter($value);
                    }
                }
            } else {
                $prefix = substr($filter, 0, 1);

                //have
                if ($prefix === '*') {
                    require_once 'Diggin/Scraper/Autofilter.php';
                    $filter = substr($filter, 1);
                    $filterds = new Diggin_Scraper_Autofilter(new ArrayIterator($values), $filter, true);
                //not have
                } elseif ($prefix === '!') {
                    $filter = substr($filter, 1);
                    $filterds = new Diggin_Scraper_Autofilter(new ArrayIterator($values), $filter, false);
                } elseif ($prefix === '/' or $prefix === '#') {
                    $filterds = new RegexIterator(new ArrayIterator($values), $filter);
                } else {
                    require_once 'Diggin/Scraper/Filter/Exception.php';
                    throw new Diggin_Scraper_Filter_Exception("Unkown prefix '$prefix' : {$e->getMessage()}");
                }

                foreach($filterds as $filterd) $return[] = $filterd;
            }

            $values = $return;
        }

        return $return;
    }
}

PHPで知っておきたいcreate_function5つのトリビア - komagataによると、create_functionで作った匿名関数はchr(0)(ヌル文字?)で始まる名前で管理されるらしい?ので、一番はじめの正規表現をヌル文字にもマッチするように書き換えてみます。

<?php
if (preg_match('/^[0-9a-zA-Z\0]/', $filter)) {

するとこんな風に書けるようになりました。

<?php
require_once 'Diggin/Scraper.php';

$func = create_function('$url', <<<FUNC
    return preg_replace('/^'.preg_quote('http://www.au.kddi.com', '/').'/', '', \$url);
FUNC
);

// サービスカテゴリのURLを取得
try {
    $url = 'http://www.au.kddi.com/service/index.html';

    $scraper = new Diggin_Scraper();
    $scraper->process('p.contentsBoxTitle a', "url[] => @href, $func")
            ->scrape($url);
    print_r($scraper->results);
} catch (Exception $e) {
    echo $e.PHP_EOL;
}

この例だとあんまりメリット無いですが…。

というわけで、こんなのいかがでしょーか。> sasezakiさん。

Diggin_Scraperでソフトバンクのサイトが処理できなくて困った(いちおう解決)

ソフトバンクの機種情報をDiggin_Scraperでスクレイピングするのに、普通の方法だとうまくいかなくていろいろ試したところ、こんな感じになりました。

<?php
require_once 'Diggin/Scraper.php';
require_once 'Diggin/Scraper/Adapter/Htmlscraping.php';
require_once 'Zend/Http/Response.php';

class Mobile_Profile_Adapter_Softbank_Attrstrip extends Diggin_Scraper_Adapter_Htmlscraping
{
    public function readData($response)
    {
        // htmlのdir属性を削る
        $body = $response->getBody();
        $body = str_replace('dir="ltr"', '', $body);

        // 内容は符号化されていないことにする
        $headers = $response->getHeaders();
        if (isset($headers['Transfer-encoding'])) {
            unset($headers['Transfer-encoding']);
        }

        $_response = new Zend_Http_Response(
            $response->getStatus(),
            $headers,
            $body
        );

        return parent::readData($_response);
    }
}

try {
    $url = 'http://creation.mb.softbank.jp/terminal/?lup=y&cat=http';

    $scraper = new Diggin_Scraper();
    $scraper->changeStrategy('Diggin_Scraper_Strategy_Flexible', new Mobile_Profile_Adapter_Softbank_Attrstrip());
    $scraper->process('//tr[@bgcolor="#FFFFFF"]/td[1]', 'model[] => TEXT')
            ->scrape($url);
    print_r($scraper->results);
} catch (Exception $e) {
    echo $e.PHP_EOL;
}
/*
Array
(
    [model] => Array
        (
            [0] => 831T
            [1] => 830T
            [2] => 931SH
            [3] => 930SC
            [4] => 930SH
            [5] => 830CA
            [6] => 830P
            [7] => 830SH s
            ......
            [183] => 304T
        )

)
*/

アダプタでレスポンスをいじってます。以下試行錯誤の記録など。


とりあえず普通にやってみた。

<?php
require_once 'Diggin/Scraper.php';

try {
    $url = 'http://creation.mb.softbank.jp/terminal/?lup=y&cat=http';

    $scraper = new Diggin_Scraper();
    $scraper->process('//tr[@bgcolor="#FFFFFF"]/td[1]', 'model[] => TEXT')
            ->scrape($url);
    print_r($scraper->results);
} catch (Exception $e) {
    echo $e.PHP_EOL;
}

実行。

$ php sample.php
exception 'Diggin_Scraper_Strategy_Exception' with message 'Couldn't find By Xpath, Process : './/tr[@bgcolor="#FFFFFF"]/td[1]', 'model => "TEXT"'' in /usr/share/php/Diggin/Scraper/Strategy/Flexible.php:96
Stack trace:
#0 /usr/share/php/Diggin/Scraper/Strategy/Flexible.php(76): Diggin_Scraper_Strategy_Flexible->extract(Object(SimpleXMLElement), Object(Diggin_Scraper_Process))
#1 /usr/share/php/Diggin/Scraper/Strategy/Abstract.php(44): Diggin_Scraper_Strategy_Flexible->scrape(Object(Zend_Http_Response), Object(Diggin_Scraper_Process))
#2 /usr/share/php/Diggin/Scraper/Context.php(32): Diggin_Scraper_Strategy_Abstract->scrapedData(Object(Diggin_Scraper_Process))
#3 /usr/share/php/Diggin/Scraper/Strategy/Abstract.php(74): Diggin_Scraper_Context->scrape(Object(Diggin_Scraper_Process))
#4 /usr/share/php/Diggin/Scraper.php(344): Diggin_Scraper_Strategy_Abstract->getValues(Object(Diggin_Scraper_Context), Object(Diggin_Scraper_Process))
#5 /home/okonomi/sample.php(36): Diggin_Scraper->scrape('http://creation...')
#6 {main}

なんか失敗。XPath式を「//body」とか絶対あるようなのにしても同じ。よくわからないので処理を追いかけてみる。

エラーを見るとDiggin/Scraper/Strategy/Flexible.phpの96行めで何かが起きているらしい(Diggin_Scraper_Strategy_Flexible::extract())。

<?php
    public function extract($values, $process)
    {
        //↓このハンドリングはxpathの記述自体が間違ってたとき(いらないかな?)
        //set_error_handler(
        //    create_function('$errno, $errstr',
        //    'if($errno) require_once "Diggin/Scraper/Strategy/Exception.php"; 
        //       throw new Diggin_Scraper_Strategy_Exception($errstr, $errno);'
        //    )
        //);

        $results = (array) $values->xpath(self::_xpathOrCss2Xpath($process->expression));
        //restore_error_handler();

        if (count($results) === 0 or ($results[0] === false)) {
            require_once 'Diggin/Scraper/Strategy/Exception.php';
            
            $process->expression = self::_xpathOrCss2Xpath($process->expression);
            throw new Diggin_Scraper_Strategy_Exception("Couldn't find By Xpath, Process : $process");
        }
        
        return $results;
    }

引数はSimpleXMLElementとDiggin_Scraper_Processで、$value->xpath()の戻り値がよろしくないと例外を送出する。

このSimpleXMLElementが取得されてるところはDiggin_Scraper_Strategy_Flexible::scrape()

<?php
    public function scrape($respose, $process)
    {
        $simplexml = $this->getAdapter()->readData($respose);
        
        return self::extract($simplexml, $process);
    }

$this->getAdapter()->readData()の戻り値がSimpleXMLElement。
$this->getAdapter()はDiggin_Scraper_Adapter_Interfaceを継承したクラスのインスタンスが取得される(デフォルトでDiggin_Scraper_Adapter_Htmlscraping)。

つぎはDiggin_Scraper_Adapter_Htmlscraping::readData()

<?php
    public function readData($response)
    {
        return $this->getXmlObject($response);
    }

さらにDiggin_Scraper_Adapter_Htmlscraping::getXmlObject()(長いので端折りまくってます)

<?php
    final public function getXmlObject($response)
    {
...
        $xhtml = $this->getXhtml($response);
...
        $responseBody = preg_replace('/\sxmlns="[^"]+"/', '', $xhtml);
...
        try {
            //@see http://php.net/libxml.constants
            if (isset($this->config['libxmloptions'])) {
                $xml_object = @new SimpleXMLElement($responseBody, $this->config['libxmloptions']);
            } else {
                $xml_object = @new SimpleXMLElement($responseBody);
            }
        } catch (Exception $e) {
            require_once 'Diggin/Scraper/Adapter/Exception.php';
            throw new Diggin_Scraper_Adapter_Exception($e);
        }
...
        return $xml_object;
    }

ここで$responseから$xhtmlが取り出されて、それを引数にSimpleXMLElementが作成されてる。$responseが何者かは分からんけど。Diggin_Scraper_Adapter_Htmlscraping::getXhtml()はHTMLの整形をしているみたい。

何も問題なさそう…じゃあHTMLの方に原因が?

Diggin_Scraper使わずにやってみる。

<?php
$url = 'http://creation.mb.softbank.jp/terminal/?lup=y&cat=http';
$content = file_get_contents($url);
$xml = simplexml_import_dom(@DOMDocument::loadHTML($content));
print_r($xml->xpath('//tr[@bgcolor="#FFFFFF"]/td[1]'));
/*
Array
(
    [0] => SimpleXMLElement Object
        (
            [0] => 831T
        )
...
*/

普通に通る。んー…。

ページのソースを見てみる。htmlタグにxmlns属性の記述が。ん、SimpleXMLとxmlnsて何か問題なかったっけ。ググるこれか!
そういえばDiggin_Scraper_Adapter_Htmlscraping::getXmlObject()でxmlnsを削除してた。じゃあやっぱり問題ないんじゃ?

分からなくなってきたので、SimpleXMLElementの引数の$responseBodyを覗いてみた。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html lang="ja" xml:lang="ja" dir="ltr" xmlns=
"http://www.w3.org/1999/xhtml">
<head>
...

xmlnsの後に改行が入ってる。このせいで正規表現にマッチしないのか?
Diggin_Scraper_Adapter_Htmlscraping::getXmlObject()のxmlnsを削除する正規表現を改行も考慮したものに書き換えてみた。

<?php
$responseBody = preg_replace('/\sxmlns=\n?"[^"]+"/', '', $xhtml);

結果

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html lang="ja" xml:lang="ja" dir="ltr">
<head>
...

xmlns属性が削除できてる。結果もちゃんと取得できた。

次はなんで改行が入ってるのかを探る。
$responseBodyを遡ってみると、Diggin_Scraper_Adapter_Htmlscraping::getXhtml()の中のtidyでHTMLを整形してるところの前後で改行が入ってた。

<?php
$responseBody = str_replace('&', '&amp;', $responseBody);
$tidy = new tidy;
$tidy->parseString($responseBody, array('output-xhtml' => true), 'UTF8');
$tidy->cleanRepair();
$responseBody = $tidy->html();

またググってみると、tidyはデフォルトで68文字で改行するらしい。改行しないようにオプションを追加してみる。

<?php
$tidy->parseString($responseBody, array('output-xhtml' => true, 'wrap' => 0), 'UTF8');

いけた。
でもDiggin本体に手を加えるのはいやなので、ほかの方法を考える。レスポンスに手を加えて改行されない程度に短くすればいいのでは?
ということでいちばん上のソースになりました(いやー長かった)。

再現コードも載せておきます。

<?php
require_once 'Diggin/Scraper.php';
require_once 'Zend/Http/Client.php';
require_once 'Zend/Http/Client/Adapter/Test.php';

$adapter = new Zend_Http_Client_Adapter_Test();
$client = new Zend_Http_Client();
$client->setAdapter($adapter);
Diggin_Scraper::setHttpClient($client);

try {
    // 例外が発生する
    $adapter->setResponse(
        'HTTP/1.1 200 OK'        ."\r\n".
        'Content-type: text/html'."\r\n".
                                  "\r\n".
        '<html lang="ja" xml:lang="ja" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">aaa</html>'
    );
    $scraper = new Diggin_Scraper();
    $scraper->process('/body', 'hoge => TEXT')
            ->scrape('http://localhost/');
    print_r($scraper->results);
} catch (Exception $e) {
    echo $e.PHP_EOL;
}

try {
    // 成功する
    $adapter->setResponse(
        'HTTP/1.1 200 OK'        ."\r\n".
        'Content-type: text/html'."\r\n".
                                  "\r\n".
        '<html><body>aaa</body></html>'
    );
    $scraper = new Diggin_Scraper();
    $scraper->process('/body', 'hoge => TEXT')
            ->scrape('http://localhost/');
    print_r($scraper->results);
} catch (Exception $e) {
    echo $e.PHP_EOL;
}

Diggin_Scraperでドコモの機種情報をスクレイピング

開発者向け情報 | サービス・機能 | NTTドコモから機種情報をスクレイピング

<?php
require_once 'Diggin/Scraper.php';

function getDevice($value)
{
    preg_match('/([^(]+)(((.*)))?/iu', $value, $match);
    $device = $match[1];

    return $device;
}

function getFont($value)
{
    preg_match_all('/((([^)]*)))?(\d+)×(\d+)/iu', $value, $match);
    $fonts = array();
    for ($i = 0; $i < count($match[0]); $i++) {
        $key = !empty($match[2][$i]) ? $match[2][$i] : $i;
        $fonts[$key] = array(
            'width'  => $match[3][$i],
            'height' => $match[4][$i],
        );
    }

    return $fonts;
}

function getCharactor($value)
{
    $value = (string)$value;
    preg_match_all('/((([^\n]*))\n?)?(\d+)/iu', $value, $match);
    $characters = array();
    for ($i = 0; $i < count($match[0]); $i++) {
        $key = !empty($match[2][$i]) ? $match[2][$i] : $i;
        $characters[$key] = $match[3][$i];
    }

    return $characters;
}

function getBrowser($value)
{
    preg_match_all('/(\d+)[^\d]+(\d+)((([^\d)]*)))?/iu', $value, $match);
    $screens = array();
    for ($i = 0; $i < count($match[0]); $i++) {
        $key = !empty($match[4][$i]) ? $match[4][$i] : $i;
        $screens[$key] = array(
            'width'  => $match[1][$i],
            'height' => $match[2][$i],
        );
    }

    return $screens;
}

function getDisplay($value)
{
    preg_match_all('/((?([^\d)]*))?)?(\d+)×(\d+)/iu', $value, $match);
    $screens = array();
    for ($i = 0; $i < count($match[0]); $i++) {
        $key = !empty($match[2][$i]) ? $match[2][$i] : $i;
        $screens[$key] = array(
            'width'  => $match[3][$i],
            'height' => $match[4][$i],
        );
    }

    return $screens;
}

function getColor($value)
{
    preg_match_all('/(白黒|カラー)(\d+)/iu', $value, $match);
    $color = array(
        'type' => $match[1][0],
        'num'  => $match[2][0],
    );

    return $color;
}

try {
    $url = 'http://www.nttdocomo.co.jp/service/imode/make/content/spec/screen_area/index.html';

    $profile1 = new Diggin_Scraper_Process();
    $profile1->process('/td[1]/span[@class="txt"]', 'device => "TEXT", getDevice')
             ->process('/td[2]/span[@class="txt"]', 'font => "TEXT", getFont')
             ->process('/td[3]/span[@class="txt"]', 'charactor => "RAW", getCharactor')
             ->process('/td[4]/span[@class="txt"]', 'browser => "TEXT", getBrowser')
             ->process('/td[5]/span[@class="txt"]', 'display => "TEXT", getDisplay')
             ->process('/td[6]/span[@class="txt"]', 'color => "TEXT", getColor');
    $profile2 = new Diggin_Scraper_Process();
    $profile2->process('/td[2]/span[@class="txt"]', 'device => "TEXT", getDevice')
             ->process('/td[3]/span[@class="txt"]', 'font => "TEXT", getFont')
             ->process('/td[4]/span[@class="txt"]', 'charactor => "RAW", getCharactor')
             ->process('/td[5]/span[@class="txt"]', 'browser => "TEXT", getBrowser')
             ->process('/td[6]/span[@class="txt"]', 'display => "TEXT", getDisplay')
             ->process('/td[7]/span[@class="txt"]', 'color => "TEXT", getColor');

    $scraper = new Diggin_Scraper();
    $scraper->process('//table/tr[@class="acenter"][count(td)=6]', array('profile[]' => $profile1))
            ->process('//table/tr[@class="acenter"][count(td)=7]', array('profile[]' => $profile2))
            ->scrape($url);
    print_r($scraper->results);
} catch (Exception $e) {
    die($e);
}

テーブルのtdの数が6か7かで記述が重複してるのがいけてない。


追記:
書き直してみた。

<?php
try {
    $url = 'http://www.nttdocomo.co.jp/service/imode/make/content/spec/screen_area/index.html';

    $profile = new Diggin_Scraper_Process();
    $profile->process('/td[last()-5]/span[@class="txt"]', 'device => "TEXT", getDevice')
            ->process('/td[last()-4]/span[@class="txt"]', 'font => "TEXT", getFont')
            ->process('/td[last()-3]/span[@class="txt"]', 'charactor => "RAW", getCharactor')
            ->process('/td[last()-2]/span[@class="txt"]', 'browser => "TEXT", getBrowser')
            ->process('/td[last()-1]/span[@class="txt"]', 'display => "TEXT", getDisplay')
            ->process('/td[last()-0]/span[@class="txt"]', 'color => "TEXT", getColor');

    $scraper = new Diggin_Scraper();
    $scraper->process('//table/tr[@class="acenter"]', array('profile[]' => $profile))
            ->scrape($url);
    print_r($scraper->results);
} catch (Exception $e) {
    die($e);
}

trが7個のときは先頭のシリーズの列を除外したいので、「最後から何個目か」を指定するようにした。すっきり。

Diggin_Scraperを使ってみる

PHP関西 勉強会で発表を聞いてからずっと気になってたDiggin_Scraperを使ってみました。以下作業ログ垂れ流し。

インストール。

$ sudo pear install http://diggin.musicrider.com/Diggin.tgz
downloading Diggin.tgz ...
Starting to download Diggin.tgz (220,588 bytes)
................done: 220,588 bytes
downloading Diggin_Scraper_Adapter_Htmlscraping.tgz ...
Starting to download Diggin_Scraper_Adapter_Htmlscraping.tgz (4,546 bytes)
...done: 4,546 bytes
__uri/Diggin_Scraper_Adapter_Htmlscraping requires PHP extension "tidy"
__uri/Diggin requires package "http://diggin.musicrider.com/Diggin_Scraper_Adapter_Htmlscraping"
No valid packages found
install failed

Diggin_Scraper_Adapter_Htmlscrapingが無いみたい?

$ sudo pear install openpear/Diggin_Scraper_Adapter_Htmlscraping-beta
downloading Diggin_Scraper_Adapter_Htmlscraping-0.3.3.tgz ...
Starting to download Diggin_Scraper_Adapter_Htmlscraping-0.3.3.tgz (6,750 bytes)
.....done: 6,750 bytes
install ok: channel://openpear.org/Diggin_Scraper_Adapter_Htmlscraping-0.3.3
$ sudo pear install http://diggin.musicrider.com/Diggin.tgz
downloading Diggin.tgz ...
Starting to download Diggin.tgz (220,588 bytes)
................done: 220,588 bytes
downloading Diggin_Scraper_Adapter_Htmlscraping.tgz ...
Starting to download Diggin_Scraper_Adapter_Htmlscraping.tgz (4,546 bytes)
...done: 4,546 bytes
__uri/Diggin_Scraper_Adapter_Htmlscraping requires PHP extension "tidy"
__uri/Diggin requires package "http://diggin.musicrider.com/Diggin_Scraper_Adapter_Htmlscraping"
No valid packages found
install failed

変わらず。tidyをインストールすればいいのか?

$ sudo pecl install -a tidy
downloading tidy-1.2.tgz ...
Starting to download tidy-1.2.tgz (9,602 bytes)
.....done: 9,602 bytes
3 source files, building
running: phpize
Configuring for:
PHP Api Version:         20041225
Zend Module Api No:      20060613
Zend Extension Api No:   220060519
 1. Tidy library installation dir? : autodetect

1-1, 'all', 'abort', or Enter to continue:
building in /var/tmp/pear-build-root/tidy-1.2
running: /tmp/pear/download/tidy-1.2/configure --with-tidy
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for a sed that does not truncate output... /bin/sed
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc and cc understand -c and -o together... yes
checking for system library directory... lib
checking if compiler supports -R... no
checking if compiler supports -Wl,-rpath,... yes
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
checking target system type... i686-pc-linux-gnu
checking for PHP prefix... /usr
checking for PHP includes... -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
checking for PHP extension directory... /usr/lib/php5/20060613+lfs
checking for PHP installed headers prefix... /usr/include/php5
checking for re2c... re2c
checking for re2c version... 0.13.3 (ok)
checking for gawk... no
checking for nawk... nawk
checking if nawk is broken... no
checking for TIDY support... yes, shared
configure: error: Cannot find libtidy
ERROR: `/tmp/pear/download/tidy-1.2/configure --with-tidy' failed

peclからのインストールはなんか失敗した。パッケージから探す。

$ aptitude search tidy | grep php
p   php5-tidy                       - tidy module for php5

$ sudo aptitude install php5-tidy

もういちどインストール。

$ sudo pear install http://diggin.musicrider.com/Diggin.tgz
downloading Diggin.tgz ...
Starting to download Diggin.tgz (220,588 bytes)
..............................................done: 220,588 bytes
downloading Diggin_Scraper_Adapter_Htmlscraping.tgz ...
Starting to download Diggin_Scraper_Adapter_Htmlscraping.tgz (4,546 bytes)
...done: 4,546 bytes
install ok: channel://__uri/Diggin_Scraper_Adapter_Htmlscraping-0.2.1
ERROR: __uri/Diggin: conflicting files found:
           Diggin/Http/Exception.php (openpear.org/diggin_scraper_adapter_htmlscraping)
Diggin/Scraper/Adapter/Exception.php (openpear.org/diggin_scraper_adapter_htmlscraping)
        Diggin/Scraper/Exception.php (openpear.org/diggin_scraper_adapter_htmlscraping)
                Diggin/Exception.php (openpear.org/diggin_scraper_adapter_htmlscraping)

ぎゃー初めて見るエラーが!Diggin_Scraper_Adapter_Htmlscrapingを個別にインストールしたのがまずかった?

$ sudo pear uninstall openpear/diggin_scraper_adapter_htmlscraping
uninstall ok: channel://openpear.org/Diggin_Scraper_Adapter_Htmlscraping-0.3.3
$ sudo pear install http://diggin.musicrider.com/Diggin.tgz
downloading Diggin.tgz ...
Starting to download Diggin.tgz (220,588 bytes)
.........................................done: 220,588 bytes
install ok: channel://__uri/Diggin-0.5.1


サンプル。

<?php
require_once 'Diggin/Scraper.php';

try {
    $scraper = new Diggin_Scraper();
    $scraper->process('a.title', "title => 'TEXT'", "url => '@href'")
            ->scrape('http://d.hatena.ne.jp/keyword/%BA%B0%CC%EE%A4%A2%A4%B5%C8%FE');
    print_r($scraper->results);
} catch (Exception $e) {
    die($e);
}

実行。

$ php sample.php
Warning: Diggin_Scraper::require_once(Zend/Http/Client.php): failed to open stream: No such file or directory in /usr/share/php/Diggin/Scraper.php on line 12

Zend Frameworkをインストールしてなかった。
Ubuntuのパッケージにあったのでそこからインストールする。バージョンが古いっぽいのが気になるけど。

$ aptitude search zend
p   libzend-framework-php                                                     - a simple, straightforward, open-source software framework for PHP 5
p   zend-framework                                                            - a simple, straightforward, open-source software framework for PHP 5

$ sudo aptitude install zend-framework

/usr/share/php/libzend-framework-phpにインストールされたので、/etc/php5/cli/php.iniにinclude_pathを追加。

実行。

$ php sample.php
Warning: require_once(Zend/Dom/Query/Css2Xpath.php): failed to open stream: No such file or directory in /usr/share/php/Diggin/Scraper/Strategy/Flexible.php on line 18

やはりバージョンが古かったか。

$ sudo aptitude remove zend-framework

php.iniも元に戻しておく。

ダウンロードページから最新版(1.7.1)をダウンロード、インストールする。

$ wget http://framework.zend.com/releases/ZendFramework-1.7.1/ZendFramework-1.7.1-minimal.tar.gz
$ tar xvfz ZendFramework-1.7.1-minimal.tar.gz
$ sudo mv ZendFramework-1.7.1-minimal/library/Zend /usr/share/php/

実行。

$ php sample.php
Warning: Diggin_Scraper_Strategy_Flexible::require_once(Diggin/Scraper/Adapter/Htmlscraping.php): failed to open stream: No such file or directory in /usr/share/php/Diggin/Scraper/Strategy/Flexible.php on line 57

Diggin_Scraper_Adapter_Htmlscrapingやっぱいんのか!

$ sudo pear install openpear/Diggin_Scraper_Adapter_Htmlscraping-beta

実行。

$ php sample.php
Warning: Diggin_Uri_Http::require_once(Net/URL2.php): failed to open stream: No such file or directory in /usr/share/php/Diggin/Uri/Http.php on line 54

・・・。

$ sudo pear install -a Net_URL2-beta

実行。

$ php sample.php
Array
(
    [title] => 紺野あさ美
    [url] => http://d.hatena.ne.jp/keyword/%ba%b0%cc%ee%a4%a2%a4%b5%c8%fe
)

キタ!


Diggin_Scraper_Adapter_Htmlscrapingのインストールでトラブったけど、改めて作業ログを見直してみると、php5-tidyをインストールしてからDigginをインストールするのが正しい手順だった気がする。

いちど全部アンインストールしてから再度インストールしてみる。

$ sudo pear uninstall __uri/Diggin_Scraper_Adapter_Htmlscraping
Notice: Undefined index:  channel in PEAR/Dependency2.php on line 910
uninstall ok: channel://__uri/Diggin_Scraper_Adapter_Htmlscraping-0.2.1
$ sudo pear uninstall openpear/Diggin_Scraper_Adapter_Htmlscraping
uninstall ok: channel://openpear.org/Diggin_Scraper_Adapter_Htmlscraping-0.3.3
$ sudo pear uninstall __uri/Diggin
uninstall ok: channel://__uri/Diggin-0.5.1
$ sudo pear install http://diggin.musicrider.com/Diggin.tgz
downloading Diggin.tgz ...
Starting to download Diggin.tgz (220,588 bytes)
.........................................done: 220,588 bytes
downloading Diggin_Scraper_Adapter_Htmlscraping.tgz ...
Starting to download Diggin_Scraper_Adapter_Htmlscraping.tgz (4,546 bytes)
...done: 4,546 bytes
install ok: channel://__uri/Diggin_Scraper_Adapter_Htmlscraping-0.2.1
install ok: channel://__uri/Diggin-0.5.1


実行。

$ php sample.php
Array
(
    [title] => 紺野あさ美
    [url] => http://d.hatena.ne.jp/keyword/%ba%b0%cc%ee%a4%a2%a4%b5%c8%fe
)

よし、だいじょうぶ。

CakePHP関西勉強会

12/12に開催されたCakePHP関西勉強会に参加してきました。

CakePHPはいちおう実務で使ってるけど全然使いこなせてる感じがしないので、何とかしたいと思ってたところでした。あと今でも本命はEthnaなので、CakePHPのいいところをEthnaのほうにもっていけたらいいなーと。

発表内容

CakePHP 処理の流れを掴む (shin1x1さん)
  • フレームワークからどう呼ばれるか
    • 「おまえが呼ぶな。俺が呼ぶ。」
  • 流れ
    1. FrontController
      • エントリポイント
    2. bootstrap
    3. Dispatcher
      • キャッシュ出力
      • dispatch();
      • _invoke();
    4. Application

チューニングの話もあってとても参考になりました(詳しいところはメモし忘れてしまいました…)。

フォームメールを生成するプラグイン (slywalkerさん)

CakePHPのプラグイン機能のお話。CakePHPってプラグイン機能があったんですね。知らなかった!

開発事例と失敗談 (keisonさん)

実際にCakePHPで開発されたときのお話。

CakePHP導入以前はHTML_QuickForm_Contrller + Smartyで開発されてたとのことで、ちょっと親近感でした。ぼくの場合は独自フレームワーク + HTML_QuickForm + SmartyからEthna(+ Smarty)ですが。

開発事例についてもうすこし詳しくはこちらで見られるみたいです。

CakePHPと私(仮) (m-takagiさん)

LL温泉@湯布院のお話。写真をスライドショーしながら旅の思い出話など…ってCakePHPぜんぜん関係ねぇ!w 最高でした。

懇親会

懇親会は韓国料理屋さんで。オフレコ話とかなにやらこわい話とかスーツvsギーク的な話とか、おもしろい話がたくさんできました。というか話のレベルについていけずほとんど聞き役でした。今の職場は良くも悪くもぬるいのかなーとか思ったりもしました。

高校時代の友達も参加してたのにはびっくりしました。いやーまさかこっち方面で仕事してるとは。

帰りに一緒になったid:zero-uhuraさんとタクシーの中でいろいろお話。ここでも興味深いお話がいろいろと。

感想など

参加できてよかったです。ありがとうございました。

次回もあればぜひ参加したいところですが、参加応募の競争率が徐々に上がってきている気がするので油断できません!でももし間に合わなかったときは発表者が足りないっていってたし発表側で参加すれば…Ethnaのことなら何か話せるかな…

ハードディスクを増設する

foltiaを動かしてるマシンのハードディスク容量がいつのまにか残り2G足らずになってたので、あわててハードディスクを増設することにした。

普通に増設してマウントすると別の領域とかになってファイルの保存先を変更したりとかで面倒くさいので、今のままで容量を増やす?(←あまり理解していない)「LVM」てのを試してみた。

OSはFedora9で。作業はrootで。

参考:asterisk Fedora - Fedora5/論理ボリューム

接続したハードディスクの認識確認

dmesg | grep sd

を実行すると「sdb」という文字が見えるので、どうやら/dev/sdbとして認識されているみたい。

パーティションの作成

Linux LVM形式でパーティションを作成。特に分割する必要ないので、新しいドライブぜんぶを1パーティションで。

fdisk /dev/sdb

細かい操作は忘れてしまったので、参考ページにて。

物理ボリューム(PV)の作成

pvcreate /dev/sdb1

ボリュームグループ(VG)に追加

コマンドラインでの方法が分からなかったのでGUIで。

yum install system-config-lvm

あとでググったら

vgextend VolGroup00 /dev/sdb1

とするらしい。

ファイルシステムの設定

追加した領域をファイルシステムとして認識させる。

resize2fs /dev/sda1

時間がかかるのでしばらく放置。

確認

df -h

すると/dev/mapper/VolGroup00-LogVol00のサイズが増えてる。やたー!