i2i無料WEBパーツ
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
8月になりましたねぇ。最近の札幌、妙に蒸し暑いので北海道らしくないなぁ~って気がしてるのは私だけかしら・・・?

このところ、preg_match_allでHTMLをスクレイピングするときに、かなーり嵌まっていたのでメモ(つ_・。)
これが、またたちの悪い現象でして・・・(汗汗

■環境
・Xampp(PHP 5.2.9)

■現象
・preg_match_all関数で全角を含むテキストを抽出しようとすると、PHPが落ちて以降、PHPプログラムが起動できない。
・httpd.exeのプロセスは生きているようだが、http://localhost/を実行してもレスポンスが帰ってこないのでApacheが死んでるッてことか。
・さしあたって、Apacheを再起動するとPHPプログラムの実行は可能になる。

■原因
 どうやら、下記のようなpreg_match_allの実行で、<a href="xxxx">○○○</a>となっている文字列があって全角の○○○をキャプチャ使用としたとき、その長さが一定値以上だと落っこちるみたい。うーむ、PHPのバグかいな・・・orz
 私の環境ですと○○○の長さが全角500文字まではOKでしたが、620文字ではNG。具体的に何文字以上がNGかは、調査してません^^;;

 【正規表現】
preg_match_all("/<[ ]{0,}a[ \n\r][^<>]{0,}(?<= |\n|\r)(?:".$match_part.")[ \n\r]{0,}=[ \n\r]{0,}[\"|']{0,1}([^\"'>< ]{0,})[^<>]{0,}>((?:(?!<[ \n\r]*\/a[ \n\r]*>).)*)<[ \n\r]*\/a[ \n\r]*>/ is", $source, $regs);

 【検索対象】
<a href="http://www.yahoo.co.jp">123456789012..(略)..7890</a>

 上記の正規表現は、phpcrawlerutils.class.phpのfindLinksで使われている正規表現なんで、phpcrawlerを日本語のサイトに使おうとすると落ちることがあるってワケですなぁ。。。

■対応
 これは、非常に悩みました・・・。/uをつけたり、正規表現を部分的に変えてみたり・・・。tidy関数を試しに使ってロジックの置き換えを検討してみたり・・・。
 結局のところ貪欲な正規表現になっている((?:(?!<[ \n\r]*\/a[ \n\r]*>).)*)を((?:(?!<[ \n\r]*\/a[ \n\r]*>).)*<span style="color:#FF0000">?</span>)に変更したらうまく動きました^^;
 これで不具合が生じないかは現在調査中ですが、さしあたって問題なく動くようになったのでとりあえず、ヨシとしていますが・・・。
 preg_match_allを使う場合は、キャプチャする文字列が全角で1000文字くらいの場合もテストしておいたほうがよさそうですねぇ。
 スクレイピング関連の処理はPHPで実装するよりRubyの方がよいかなぁ?preg_match_allはざっくりapacheが落ちるので恐ろしいわ・・・(・_・;)

■参考サイト
うむ・・・情報は少ないし明確な対応も無いようですなぁ。zuzaraが参考になったかな。
あと、結構、preg_match_allで嵌まるケースが多いみたいですねぇ・・・。

zuzara Pukiwikiのページ表示時にapacheのプロセスがSegmentation faultする

自作プラグイン/cvscheck.inc.php
スポンサーサイト
S2Dao.PHPのselect系のメソッドは戻り値が多彩なので、サンプルプログラムによる調査とメモ。

下記のサイトにあるようにメソッド名の最後をList,Array,Yaml,Jsonとするだけでそれぞれ戻り値の形式を変えることができます。
この辺は、便利ですかね。

S2Dao.PHP5リファレンス-メソッドの定義

ただし、Yamlを使うには、Spyc(http://spyc.sourceforge.net/)をダウンロードして読み込んでおく必要があるので注意が必要ですかね。

・BlogDao.PHP
<?php
interface BlogDao {
const BEAN = "BlogEntity";
public function findAllList();
public function findAllArray();
public function findAllYaml();
public function findAllJson();
}


・サンプルプログラム S2daotestReturnValueTest.php
<?php
require_once dirname(__FILE__) . "/example.inc.php";
require_once 'Zend/Debug.php';
require_once 'includes/spyc.php';

$container = S2ContainerFactory::create("./resource/example.dicon");
$dao = $container->getComponent("BlogDao");

Zend_debug::dump($dao->findAllArray(), 'fildAllArry:',TRUE);
Zend_debug::dump($dao->findAllList(), 'fildAllList',TRUE);
Zend_debug::dump($dao->findAllYaml(), 'fildAllYaml:',TRUE);
Zend_debug::dump($dao->findAllJson(), 'fildAllJson:',TRUE);

?>


・サンプルプログラムの結果
fildAllArry: array(2) {
[0] => array(1) {
["BlogEntity"] => array(7) {
["blogid"] => string(2) "20"
["BLOGID"] => string(2) "20"
["blogId"] => string(2) "20"
["title"] => string(22) "KJの業務日誌 "
["TITLE"] => string(22) "KJの業務日誌 "
["url"] => string(36) "http://kevinjohnson2.blog69.fc2.com/"
["URL"] => string(36) "http://kevinjohnson2.blog69.fc2.com/"
}
}
[1] => array(1) {
["BlogEntity"] => array(7) {
["blogid"] => string(2) "21"
["BLOGID"] => string(2) "21"
["blogId"] => string(2) "21"
["title"] => string(18) "KJの航海日誌 "
["TITLE"] => string(18) "KJの航海日誌 "
["url"] => string(35) "http://kevinjohnson.blog71.fc2.com/"
["URL"] => string(35) "http://kevinjohnson.blog71.fc2.com/"
}
}
}

fildAllList object(S2Dao_ArrayList)#448 (2) {
[0] => object(BlogEntity)#445 (3) {
["blogId:protected"] => string(2) "20"
["title:protected"] => string(22) "KJの業務日誌 "
["url:protected"] => string(36) "http://kevinjohnson2.blog69.fc2.com/"
}
[1] => object(BlogEntity)#446 (3) {
["blogId:protected"] => string(2) "21"
["title:protected"] => string(18) "KJの航海日誌 "
["url:protected"] => string(35) "http://kevinjohnson.blog71.fc2.com/"
}
}

fildAllYaml: string(500) "---
-
BlogEntity:
blogid: 20
BLOGID: 20
blogId: 20
title: KJの業務日誌
TITLE: KJの業務日誌
url: http://kevinjohnson2.blog69.fc2.com/
URL: http://kevinjohnson2.blog69.fc2.com/
-
BlogEntity:
blogid: 21
BLOGID: 21
blogId: 21
title: KJの航海日誌
TITLE: KJの航海日誌
url: http://kevinjohnson.blog71.fc2.com/
URL: http://kevinjohnson.blog71.fc2.com/
"

fildAllJson: string(505) "[{"BlogEntity":{"blogid":"20","BLOGID":"20","blogId":"20","title":"\uff2b\uff2a\u306e\u696d\u52d9\u65e5\u8a8c ","TITLE":"\uff2b\uff2a\u306e\u696d\u52d9\u65e5\u8a8c ","url":"http:\/\/kevinjohnson2.blog69.fc2.com\/","URL":"http:\/\/kevinjohnson2.blog69.fc2.com\/"}},{"BlogEntity":{"blogid":"21","BLOGID":"21","blogId":"21","title":"KJ\u306e\u822a\u6d77\u65e5\u8a8c ","TITLE":"KJ\u306e\u822a\u6d77\u65e5\u8a8c ","url":"http:\/\/kevinjohnson.blog71.fc2.com\/","URL":"http:\/\/kevinjohnson.blog71.fc2.com\/"}}]"


うむ、クライアント側とのやり取りにあわせて形式を変えればOKなんで便利かしら。

それと、今回のサンプルで使用したZend_debugは、配列なんかを整形してタグも置換して出力してくれるのでデバッグに重宝しますわ。

Flex+PHP(AMFPHP+S2Dao.PHP+S2Pager.PHP)でDataGridのページングを実装するの続きですが、S2Dao_PagerSupportでセッションから取得したDtoのクラスが__PHP_Incomplete_Classとなってしまって、結構、嵌まりました・・・。
ようは、セッションにオブジェクトを入れるときは注意してねって話のようですが・・・。

対応が正しいかどうかは微妙ですが、以下、備忘録。

■現象
下記のようにS2Dao_PagerSupportを生成して、2回検索を実行するとセッションから取得してきたDTOのオブジェクトが__PHP_Incomplete_Classとなってしまいます。

$pager = new S2Dao_PagerSupport(5, 'BlogPagerCondition', 'blogPagerCondition');
$dto = $pager->getPagerCondition();
$o_log->log('dto class name :' . get_class($dto), Zend_Log::DEBUG);


1回目のdtoのクラスは、セッション情報がないので下記のようにコンディションクラスなんですが、

2009-07-16T14:39:54+09:00 DEBUG (7): dto class name :BlogPagerCondition


2回目の検索で取得したdtoは、下記のようになっています。
2009-07-16T14:40:04+09:00 DEBUG (7): dto class name :__PHP_Incomplete_Class


■対応
S2Dao_PagerSupportを修正し、セッションにDtoをセットするときにserialize、セッションからDtoを取得するときにunserializeを実行する。

session_start();の前に対象のオブジェクトをrequire_onceで呼び出せばOKって情報があったんですけど、
S2Dao_PagerSupportのsession_start()の前で、BlogPagerConditionを呼び出しても変化なし・・・。
結局、Web Artisan Blogを参考にしserialize-unserializeを実行したところ、うまくいきました。感謝感謝でございます。


・S2Dao_PagerSupportCopy.php
<?php
class S2Dao_PagerSupportCopy
{
/** 最大取得件数 */
private $limit;

/** 検索条件クラス名 */
private $pagerConditionClass;

/** 検索条件オブジェクトのセッション中の名前 */
private $pagerConditionName;

/**
* コンストラクタ
* セッションを開始して、指定された最大取得件数,検索条件クラス名,
* 検索条件オブジェクトのセッション中の名前で新しいPagerSupportインスタンスを生成します
* @param limit 最大取得件数
* @param pagerConditionClass 検索条件クラス名
* @param pagerConditionName 検索条件オブジェクトのセッション中の名前
*/
public function __construct($limit, $pagerConditionClass, $pagerConditionName)
{
if (!headers_sent() && session_id() == '') {
session_start();
}
$this->limit = $limit;
$this->pagerConditionClass = $pagerConditionClass;
$this->pagerConditionName = $pagerConditionName;
}

/**
* Session中の検索条件オブジェクトのoffsetを更新します
* @param offset 更新Offset
*/
public function updateOffset($offset)
{
$pagerCondition = $this->getPagerCondition();
if (empty($offset)) {
$offset = 0;
}
$pagerCondition->setOffset($offset);
}

/**
* セッション中の検索条件オブジェクトを取得します。
* 検索条件オブジェクトが存在しない場合、新規に検索条件オブジェクトを生成します。
* @return 検索条件オブジェクト
*/
public function getPagerCondition()
{
$dto = $_SESSION[$this->pagerConditionName];

if (empty($dto)) {
$refClass = new ReflectionClass($this->pagerConditionClass);
$dto = $refClass->newInstance();
$dto->setLimit($this->limit);
//ここで、serialize
$_SESSION[$this->pagerConditionName] = serialize($dto);
return $dto;
}
else
{
//ここで、unserialize
return unserialize($dto);
}
//return $dto;
}

}
?>



■感想
今回も、S2xxx.PHP関連のTipsが少なくて、苦労しちゃいました・・・(つ_・。)
使っている人が少ないのかな・・・いないのか(ぇ
S2xxx.PHP関連は、おじさんみたいな素人向きじゃないのかな(汗

とはいえ、S2Pager.PHPとか便利な仕組みなので使わないのは勿体ないですねぇ~
S2Daoのコミッタ(いるのかしら?)の方には頑張ってS2Containerの最新版に合わせてほしいところですが・・・。

■参考サイト
Web Artisan Blog - ウェブ アルチザン ブログ-PHP:オブジェクト(クラス)をセッションで受け渡しする方法:serialize():シリアル化

れぶろぐ __PHP_Incomplete_Class の対処法

S2Dao_PagerSupport - セッションへの検索条件の格納をサポート

Flex+S2Dao.PHPを使おうとなると、Zend_Amfじゃあなくて、AMFPHPのほうが便利かな?と思いAMFPHPの勉強がてら、DataGridのページングを実装してみました。
これが、また苦労の連続でしたが・・・(つ_・。)
まあ、Flexもあまり詳しくないし、PHPも初心者なんでしょうがないといえばしょうがないのですが。

以下、備忘録です。

■やりたいこと
Flex+PHPでページング処理を実行したいワケ。

■環境
・Flex 3.2
・PHP 5.2.9
・MySQL 5.1
・S2Container.PHP 1.1.2
・S2Dao.PHP 1.1.2
・S2Pager.PHP

■プログラムの構成
<Flex>
・BlogListAmfphp.xml
 →BLOGテーブルの一覧を取得する画面。
・DataGridPagingControl.as
 →DataGridをページングする為のコントロール。
・CustomEvent.as
 →DataGridPagingControl.asのイベント発生時に、パラメータを設定できるようにしたEventの拡張クラス。
・main.css
 →おもにDataGridPagingControl.asを装飾するCSSファイル

<PHP>
・BlogService.PHP
 →BLOGテーブルにアクセスし、データを取得する。一覧データとページング情報をクライアントに返します。
・BlogPagerCondition.php
 →S2Dao_DefaultPagerConditionを継承した検索条件です。検索条件ってほぼEntityと同じなんだけど、多重継承できないからEntityを継承できないわけで・・・。
  まあ、大体はEntityをコピペして作成すんのかねぇ・・・。
・S2Dao_PagerSupportCopy.php
 →S2Dao_PagerSupport.phpを勝手にコピーして修正したクラス。セッション情報から取得したオブジェクトが__PHP_Incomplete_Classとならないように修正した(つもり)。
  拡張して使えばいいんじゃね?って話もあるが、プロパティがPrivateなんでコピーしてみた(汗

■画面
・初期表示
00初期画面
・検索後
01検索後
・ページング
02.jpg

■感想
 検索中のボタン表示制御とか色々つくり込まなければならないところはあるけれど、サンプルとしてはこんな感じかしら?
 ページコントロールは、ラベルじゃなくてボタンにすればよかったかなぁ・・・。Flex側のパッケージも適当すぎるし・・・orz。

・まず、AMFPHPもZend_Amfと同じような使いかたかな。PHP側の処理がまずくて下記のようなエラーがよくでます。対応としては、PHP側の処理が正しいかとにかくログを出力して検証するですね。
Error #2044: ハンドルされていない NetStatusEvent : level=error, code=NetConnection.Call.BadVersion
at BlogListAmfphp/getCon()[C:\project\flextest\BlogListAmfphp.mxml:64]
at BlogListAmfphp/search()[C:\project\flextest\BlogListAmfphp.mxml:40]
at BlogListAmfphp/__iconButton_click()[C:\project\flextest\BlogListAmfphp.mxml:112]


・次に、S2Pager.PHPに嵌まるわけ。テストクラスから実行した時には問題ないんだけど、Flexからサービスを呼び出すとこけるとか。。。
 その対応で、S2Dao_PagerSupportを修正して使ったんだけど、正しい対応かどうかは・・・自信なし!(ぇ
 詳細は明日以降の記事に書くとしますね。

嵌まりポイントは多々ありましたが、なんとかページングできました。S2Pager.PHP、いい感じですね^^

■参考サイト

PHPPRO AMFPHPとは? PHPとFlexの連携 (その1)

amfphp

amfphp download

S2Dao_PagerSupport - セッションへの検索条件の格納をサポート

ソースは長いので続きの方に記述してあります。。。
続きを読む
引き続き、Zend_AmfをつかってDBの値をDataGridに反映させてみようかなぁ~と思ったんだけど、
今まで調査してきたS2Dao.PHPをZendFramework環境でつかうのはどうもなぁ・・・。

かといって、Zend_DBをまた調査するのも面倒・・・。

ということで、Zend_Amfはちょっとおいておいて、AMFPHPにちょっかいを出してみようかと(ぇ

まあ、ぶっちゃけ、S2Dao.PHPを捨てて、Zend_Dbに早めにシフトしておいたほうが吉ってきがするんだけどねぇ・・・S2Dao.PHPはコミットがストップして久しいですし・・・(汗
しかしながら、個人的に遊んでいるだけなので、しばらくはS2Dao.PHPで突っ走る方向で(ぉ

今回の記事では、これまでにZend_Amfを調査していて、面白いサイトがあったのでメモだけしときます。

■Flex with full Zend Framework on Adobe Developer Connection
http://tv.adobe.com/#vi+f1472v1075
Zend_Amfは簡単ですよ~という外人さんの動画です。勿論、英語ですがわかりやすいですわ。
そんなに、長くはないのでZend_Amf初心者は最初に見ておいてもいいかも。

■Flex with full Zend Framework on Adobe Developer Connection (part 2)
http://www.riaspace.net/category/examples/
上記のパート2版です。なぜが、動画に直接とべませんでした・・・。

■ZendAmfDs
http://www.google.co.jp/codesearch/p?hl=ja&sa=N&cd=3&ct=rc#3VO39_-tkSQ/trunk/php/library/ZendAmfDs/ZendAmfDs.php&q=Zend/Amf/Server.php

Zend_Amf_Serverを拡張してDBを簡単にFlexで操作できるようにしたライブラリ。
ソースを一通り眺めてみましたが、勉強になりますなぁ。さらに、かなり便利そう。
configフォルダのdata-service.xmlに下記のようにクエリを書いてけばFlexからDBを操作できるんですねえ。

data-services.xml - trunk/php/application/config
<?xml version="1.0" encoding="utf-8"?>
<service>
<destination id="ZendAmfDs">
<properties>
<defaults>
<columnNameCamelSeparator>_</columnNameCamelSeparator>
<modelsPath>models</modelsPath>
</defaults>

<select id="findFooById">
<asClass>Foo</asClass>
<modelClass>Foo</modelClass>
<where>ID = 1</where>
</select>

<select id="findAllFoos">
<asClass>Foo</asClass>
<modelClass>Foo</modelClass>
</select>

<insert id="insertFoo">
<asClass>Foo</asClass>
<modelClass>Foo</modelClass>
</insert>

</properties>
</destination>

</service>


でもって、Flexのコードはこんな感じ

ZendAmfDsQuickstart.mxml - trunk/php/src
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#FFFFFF, #FFFFFF]" xmlns:models="quickstart.models.*"
xmlns:zendAmfDs="net.riaspace.zendAmfDs.*" initialize="findAllFoos()">

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;

[Bindable]
private var allFoos:ArrayCollection;

private function findAllFoos():void
{
zendAmfDs.query("findAllFoos", null, allFoosResultHandler);
}

private function allFoosResultHandler(event:ResultEvent):void
{
allFoos = new ArrayCollection(event.result as Array);
}

private function insertFoo():void
{
zendAmfDs.query("insertFoo", [foo], insertFooResultHandler);
}

private function insertFooResultHandler(event:ResultEvent):void
{
findAllFoos();
}

]]>
</mx:Script>

<mx:RemoteObject id="remoteObject" destination="zend" fault="Alert.show(event.message.toString())"/>

<zendAmfDs:Service id="zendAmfDs" remoteObject="{remoteObject}" />

<mx:DataGrid dataProvider="{allFoos}">
<mx:columns>
<mx:DataGridColumn headerText="ID" dataField="id"/>
<mx:DataGridColumn headerText="NAME" dataField="name"/>
<mx:DataGridColumn headerText="FOO_NUMBER" dataField="fooNumber"/>
</mx:columns>
</mx:DataGrid>

<models:Foo id="foo"
name="{txtName.text}"
fooNumber="{Number(txtFooNumber.text)}" />

<mx:Form>
<mx:FormItem label="NAME">
<mx:TextInput id="txtName"/>
</mx:FormItem>

<mx:FormItem label="FOO_NUMBER">
<mx:TextInput id="txtFooNumber"/>
</mx:FormItem>

<mx:FormItem>
<mx:Button label="Insert" click="insertFoo()" />
</mx:FormItem>
</mx:Form>

</mx:Application>


・ZendAmfDs SVN
http://zendamfds.googlecode.com/svn/
上記のSVNのURLです。チェックアウトして動かしてみても面白いですね。

Zendは海外にも有益な情報が多いですねぇ。
>>次のページ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。