忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2026/04/26 19:45 |
JavaScript2-9:うにょーんと左右に動く画像ビューア

よくある左右に動くサムネイルです。


jQueryのプラグイン、jCarouselがそのまんまの機能を持っていますのでさくっと作ってみます。
jCarousel 0.2.3
http://sorgalla.com/projects/jcarousel/

libフォルダがライブラリ本体、skinsフォルダが見た目のCSS、imagesフォルダがロード中の画像です。
examplesフォルダには例が入っています。

使い方は簡単で、まず<ul><li>で画像の要素を並べ、<ul>のIDをオンロードでjcarouselに突っ込むだけです。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" 
            src="jcarousel/lib/jquery.jcarousel.pack.js"></script>
        <link rel="stylesheet" type="text/css" 
            href="jcarousel/lib/jquery.jcarousel.css" />
        <link rel="stylesheet" type="text/css" 
            href="jcarousel/skins/tango/skin.css" />
        
        <script type="text/javascript">
            //オンロードでjcarouselを実行
            jQuery(document).ready(function() {
                jQuery('#id_mycarousel').jcarousel({
                     scroll: 1
                    ,initCallback:jcarousel_initCallback
                });
            });
            
            //コールバック関数が必要なら書く
            function jcarousel_initCallback(carousel, state){}
            
        </script>
    
    </head>
    <body>
        
    <ul id="id_mycarousel" class="jcarousel-skin-tango">
        <li><img src="a.jpg" width="75" height="75" alt="" /></li>
        <li><img src="b.jpg" width="75" height="75" alt="" /></li>
        <li><img src="c.jpg" width="75" height="75" alt="" /></li>
        <li><img src="d.jpg" width="75" height="75" alt="" /></li>
        <li><img src="e.jpg" width="75" height="75" alt="" /></li>
        <li><img src="f.jpg" width="75" height="75" alt="" /></li>
        <li><img src="g.jpg" width="75" height="75" alt="" /></li>
        <li><img src="h.jpg" width="75" height="75" alt="" /></li>
        <li><img src="i.jpg" width="75" height="75" alt="" /></li>
        <li><img src="j.jpg" width="75" height="75" alt="" /></li>
    </ul>
        
    </body></html>

まあ、同封されているサンプルそのまんまなのですが、このように簡単に表示が行えます。
引数で動作をある程度制御することができます。
scroll: 1で一枚づつスクロール、vertical:trueで縦表示、というふうに指定します。

それ以上複雑なことが行いたい場合は、色々な状態についてコールバック関数を指定することができます。
例として挙げているinitCallbackは、読み込まれた時に一回だけ実行してくれます。

見た目はCSSで変更します。
skins.cssを適当にいじくるだけです。
デフォルトではjcarousel-skin-tangoクラスとなっていますが、その中のサブクラスjcarousel-containerで全体のコンテナを設定、jcarousel-itemで個々のアイテムについての設定といったかんじになっています。

どうでもいいのですが、表示される要素には別に画像だけではなく文字を入れることもできるので、ちょっと使いにくいメニューなんてものも作成できます。
意味はありませんが。

 

PR


2009/03/19 17:55 | Comments(0) | TrackBack() | JavaScript
PHP1-61:PHPでセッション書き換えその2

前回の続き。
Pear::HTTP_Session2の中を見てみると、session_set_save_handler()関数を使用しています。
http://jp.php.net/manual/ja/function.session-set-save-handler.php
このsession_set_save_handlerはsession_start()とか$_SESSIONとかの動作を自由に変更できるという素敵関数です。
早速使ってみましょう。

session_handler.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
    
class SessionHandler{
 
    //セッションテーブルの定義
        protected $session_table='user_session';
        protected $session_dsn=array(
            'server'=>'localhost'
            ,'database'=>'session'
            ,'table'=>'sessiondata'
            ,'user'=>'testuser'
            ,'pass'=>'testpass'
        );
    //セッション保持時間(秒)
        protected $session_gc=3600;
    
    //MySQLリソース
        protected $session='';
    
    //コンストラクタ
    public function __construct(){
        //セッション関連ini設定
            ini_set('session.gc_probability', 50);
            ini_set('session.save_handler', 'user');
        //セッションハンドラ
            session_set_save_handler(
                 array($this,'_admin_ses_open')
                ,array($this,'_admin_ses_close')
                ,array($this,'_admin_ses_read')
                ,array($this,'_admin_ses_write')
                ,array($this,'_admin_ses_destroy')
                ,array($this,'_admin_ses_gc')
            );
        //スクリプト終了時にセッション終了
            register_shutdown_function('session_write_close');
        //セッションスタート
            session_start();
        //セッションID変更
            return $this->_regenerateID();
    }
    
    //セッションスタート
    function _admin_ses_open($save_path, $session_name){
        //DB接続
            $this->session=mysql_connect(
                 $this->session_dsn['server']
                ,$this->session_dsn['user']
                ,$this->session_dsn['pass']
            );
            mysql_select_db($this->session_dsn['database'],$this->session);
        return true;
    }
    
    //セッションクローズ
    function _admin_ses_close(){
        return TRUE;
    }
    
    //セッション読み込み
    function _admin_ses_read($id){
        
        //SQL
            $sql ="SELECT * FROM ".$this->session_dsn['table'];
            $sql.=" WHERE id = '".mysql_real_escape_string($id)."'";
            $sql.=" AND expiry > UNIX_TIMESTAMP(now()) ";
            
            $ret =mysql_query($sql,$this->session);
        
        //結果返却
            if($ret && (mysql_num_rows($ret) == 1)){
                $tmp = mysql_fetch_assoc($ret);
                return $tmp['data'];
            }
            return '';
    }
    
    //セッション書き込み
    function _admin_ses_write($id, $session_data){
        //セッション破棄時間
            $expiry=time()+$this->session_gc;
        
        //SQL
            $sql ="REPLACE INTO ".$this->session_dsn['table'];
            $sql.=" (id, data, expiry) ";
            $sql.=" VALUES ( ";
            $sql.="'".mysql_real_escape_string($id)."'";
            $sql.=", '".mysql_real_escape_string($session_data)."'";
            $sql.=", '".mysql_real_escape_string($expiry)."'";
            $sql.=" ) ";
        //書き込み
        return mysql_query($sql,$this->session);
    }
    
    //セッション削除
    function _admin_ses_destroy($id){
        //SQL
            $sql ="DELETE FROM ".$this->session_dsn['table'];
            $sql.=" WHERE id = '".mysql_real_escape_string($id)."'";
        //実行
            return mysql_query($sql,$this->session);
    }
    
    //ガベージコレクション
    function _admin_ses_gc($maxlifetime){
        //本来はmaxlifetimeから有効期限を算出するが、
        //今回は書き込み時に有効期限を記入しているので不要
        //SQL
            $sql =" DELETE FROM ".$this->session_dsn['table'];
            $sql.=" WHERE expiry < UNIX_TIMESTAMP(NOW()) ";
        return mysql_query($sql,$this->session);
    }
    
    //セッションIDを毎回変更
    function _regenerateID(){
        //古いセッションID
            $session_id_old=session_id();
        //初めてなら変更不要
            if(!$session_id_old){
                return true;
            }
        //新しいセッションID
            session_regenerate_id(true);
            $session_id_new=session_id();
            return true;
        //セッション変更    //5.1.0以前でsession_regenerate_idにtrueが使えない場合
            //$sql =" UPDATE ".$this->session_dsn['table'];
            //$sql.=" SET id='".mysql_real_escape_string($session_id_new)."'";
            //$sql.=" WHERE id = '".mysql_real_escape_string($session_id_old)."'";
            //return mysql_query($sql,$this->session);
    }
    
#クラスのおわり
}

データベースは前回のを流用しています。

session_set_save_handlerの引数は、各状態の時に実行する関数名を与えるようになっています。
引数を配列で与えると、クラス名::ファンクション名を実行してくれます。
というわけで各関数を実装してsession_set_save_handlerを呼び出すと、セッションが始まったり終わったり値を入れたり出したりするときにそれらの各関数を実行してくれます。

SessionHandler::_regenerateIDは何をやっているかというと、SessionFixation対策です。

PHPのセッションは存在しないセッションIDがやってきた場合そのIDでセッションを始めてしまいます。
よって、攻撃者が誰かにindex.php?PHPSESSID=abcdefgというアドレスをメールで送りつけ、その人がアドレスをクリックしてログインしたりした場合、攻撃者はindex.php?PHPSESSID=abcdefgからログイン無しで内部にアクセスできてしまいます。
それを防止するため、session_regenerate_id()で毎回セッションIDを変更しています。
が、session_regenerate_idは古いセッションをそのままにしてしまいます。
古いセッションを削除するsession_regenerate_id(true)は5.1.0で搭載されたので、それ以前の場合は手動で削除しなければいけません。

ちなみにリンク先では
>このような動作をする理由がいまいち把握しきれていないのだが、これは明らかにアプリケーションの問題だろう。
とか書いてありますが、PHPの場合は仕様です。

さて、このクラスの使用方法はnewするだけです。
特に引数や返り値は要りません。

session_handler.php
1
2
3
4
5
6
7
8
<?php
    
    //セッション変更
        require_once('session_handler.class.php');
        new SessionHandler();
    
    //以降普通にセッションが使える
        $_SESSION['a']='b';


以後普通にセッションがDBに書き込まれます。
汎用的に使い回し対場合はDSNなんかを引数にすればいいかもしれません。
ちなみに全くどうでもいいのですが、PHPのインスタンス変数の初期値には固定値が使えません。
protected $session_gc=60*60;とか書くと怒られます。

 



2009/03/16 18:40 | Comments(0) | TrackBack() | PHP
PHP1-60:PHPでセッション書き換え

HTTP_Session2 0.7.2 (beta)
http://pear.php.net/package/HTTP_Session2
MDB2 2.5.0b2 (beta)
http://pear.php.net/package/MDB2
MDB2_Driver_mysqli 1.5.0b2 (beta)
http://pear.php.net/package/MDB2_Driver_mysqli

PHPのセッションはデフォルトではファイルで管理されています。
/tmpやC:\tmp等に、
sess_b84f39d3a34984b515ea715226f1b6fc
といったファイル名で保存されています。
中は単なるテキストファイルで、覗いてみると、$_SESSION['data']='hogehoge';等と書いた内容がそのまま記録されているのがわかります。
その後Cookieにb84f39d3a34984b515ea715226f1b6fcと入れて行くと、該当のファイルが読み込まれてセッションを継続することができます。

で、例えば2台のサーバでロードバランシングを行っている場合等、セッションをローカルに保存していると問題が起こります。
一回目のリクエストが片方のサーバに行き、次のリクエストがもう一方のサーバに行ってしまうと、当然そっちのサーバにはセッションファイルが無いので読み込まれません。
セッションが途切れてしまうわけです。
というわけでこのような問題の回避方法として、セッションの保存方法を書き換えることができます。
Pear::HTTP_Session2を使用してセッションをデータベースに保存してみましょう。

方針としては、2台のサーバでHTTPリクエストを処理しつつ、データベースは同じ一台を見るのでセッション自体は継続する、ということになります。
まあ今回はローカルなのでサーバも一台ですが。

HTTP_Session2はバックエンドとしてPear::DB、MDB2、Memcacheに対応していますが、Memcacheは意味がないのでMDB2を使用します。
入っていない場合は適当にpear installしましょう。

まず以下のSQLを実行し、データベースを作成しておきます。

CREATE DATABASE `session`;
CREATE DATABASE
CREATE TABLE sessiondata (
    id     varchar(32) NOT NULL,
    expiry int(10),
    data   text,
    PRIMARY KEY (id)
);


そうしたらHTTP_Session2を実行。
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
 
    //require
        require_once 'HTTP/Session2.php';
    
    //URL、cookieでのセッションを禁止
        HTTP_Session2::useTransSID(false);
        HTTP_Session2::useCookies(false);
    
    //MDB2を使用
        HTTP_Session2::setContainer(
             'MDB2'
            ,array('dsn' => 'mysql://testuser:testpass@localhost/session'
            ,'table' => 'sessiondata')
        );
    
    //その他設定
        HTTP_Session2::start('s');
        HTTP_Session2::setExpire(time() + 60);    //有効期限
        HTTP_Session2::setIdle(time() + 5);        //アイドル時間
    
    //期限切れなら破棄
        if (HTTP_Session2::isExpired()) {
            HTTP_Session2::destroy();
        }
        if (HTTP_Session2::isIdle()) {
            HTTP_Session2::destroy();
        }
    //アイドル時間更新
        HTTP_Session2::updateIdle();
    
    //普通にセッションが使える
        $_SESSION['sample']='あいうえお';


以後は普通に$_SESSION['a']='b';とか書くと、それがセッションファイルではなくDSNで指定したDBに記入されます。
セッションの挙動を書き換えることに成功しました。
簡単ですね。
万一文字化けしたらCREATE TABLEやset namesなんかで適当にどうにかしてください。

 



2009/03/12 11:57 | Comments(0) | TrackBack() | PHP
レベルUPしまくりの「ドラクエ」サウンド!
http://www.news2u.net/NRR200945550.html?ref=amn
>なんとその数、94回。つまり一回聴くだけでレベルが94にまで上がった気分に浸れる。

94回レベルアップしたらレベル95じゃねえ?


2009/03/10 23:13 | Comments(0) | TrackBack() | 戯言
JavaScript2-8:JavaScriptで訪問履歴を取得

JavaScriptではテキストの状態を取得・操作する各種プロパティやメソッドがありますが、その中のひとつにテキストの色を操作するものがあります。
このcss(name)を<a>タグに使ってみるとどうなるかというと、リンク先を訪問済の場合と未訪問の場合で違う色が返ってくるわけです。

さて、Livedoor ad4Uというサービスがありますが、このサービスは上記を利用して訪問履歴を調べています。
http://ad4u.drecom.co.jp/
http://www.drecom.co.jp/pr/release/20080711/
http://takagi-hiromitsu.jp/diary/20081211.html



2009/03/09 15:31 | Comments(2) | TrackBack() | JavaScript
PHP1-59:PHPで暗号化

暗号化は大別すると2種類があります。
すなわち秘密鍵形式と公開鍵形式です。
復号可能か否かじゃないのか、と言われれば復号化できないのは暗号化ではなくハッシュなので違います。

今回は取り扱いが簡単な秘密鍵形式の暗号化を行ってみます。
秘密鍵形式の暗号化とは、いわゆる暗号化です。
'A'を'D'に変換するカエサル暗号を始め、暗号化のキーがわかればたちどころに復号化できるという暗号化です。

予め両者が暗号化キーを知っておく必要があるため、汎用的な暗号化としては適していません。
最初にキーを送る時点では暗号化できないので、ネットから送った時点でそれを傍受されてしまうと無駄になってしまうからです。
ただ、特定の会社間、会社と社員間など、ネットを介さずにキーを設定できる経路であれば十分に役立ちます。

まあとりあえず使ってみましょう。
PHPではMcryptやPEAR::Crypt_Blowfishパッケージがあります。
http://jp.php.net/manual/ja/book.mcrypt.php
http://pear.php.net/package/Crypt_Blowfish
今回はPHPモジュールなんで早そうなMcryptを使用してみます。

暗号化を行う関数であるところのmcrypt_encryptですが引数が多くてよくわかりません。

$cipherは暗号化形式です。
http://jp.php.net/manual/ja/mcrypt.ciphers.php
残念ながらAESは無いみたいなので3DESを使用します。

$keyはいわゆる暗号化キーです。
$dataは暗号化する文字列です。

$modeは暗号化メカニズムらしいです。
http://jp.php.net/manual/ja/mcrypt.constants.php
http://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E5%88%A9%E7%94%A8%E3%83%A2%E3%83%BC%E3%83%89
http://www.triplefalcon.com/Lexicon/Encryption-Block-Mode-1.htm
下のサイトがわかりやすいですが、EBCだと暗号化の単位が完全にブロックで分かれているため、解読されやすかったりブロック丸ごと入れ替えられたりという攻撃に会いやすいようです。

$ivはマニュアルだとなんのことだかわかりません。
例えば上記CBCを使用すると、現ブロックの暗号化を行うときに前のブロックのデータを使用して暗号化します。
その場合、最初のブロックには前のブロックがないので、そのかわりに$ivを与え、それを使用して最初の暗号化を行います。

mcript.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
    
class Mcrypt{
    
    //暗号で使う変数
        private $enc=array(
             'cipher'=>MCRYPT_3DES
            ,'key'=>''
            ,'mode'=>MCRYPT_MODE_CBC
            ,'iv'=>''
        );
    //暗合する文字列
        private $enc_data='';
    //復号する文字列
        private $dec_data='';
    
    //コンストラクタ
    public function __construct($key){
        //暗号化キー
        $this->enc['key']=$key;
    }
    
    //暗号化
    public function encrypt($data){
        //平文
        $this->enc_data=$data;
        //IV
        if(empty($this->enc['iv'])){
            $this->setIV();
        }
        //暗号化
        $this->val=mcrypt_encrypt(
             $this->enc['cipher']
            ,$this->enc['key']
            ,$this->enc_data
            ,$this->enc['mode']
            ,$this->enc['iv']
        );
        return $this->val;
    
    }
    //復号化
    public function decrypt($data,$iv=false){
        //暗号文
        $this->dec_data=$data;
        //IV
        if($iv){
            $this->enc['iv']=$iv;
        }
        //戻す
        $this->val=mcrypt_decrypt(
             $this->enc['cipher']
            ,$this->enc['key']
            ,$this->dec_data
            ,$this->enc['mode']
            ,$this->enc['iv']
        );
        return $this->val;
    }
    
    //IV取得
    public function getIV(){
        if(empty($this->enc['iv'])){
            return $this->_setIV();
        }else{
            return $this->enc['iv'];
        }
    }
    
    //IVセット
    public function setIV(){
        $ivsize=mcrypt_get_iv_size(
            $this->enc['cipher'],$this->enc['mode']
        );
        $this->enc['iv']=mcrypt_create_iv($ivsize,MCRYPT_RAND);
    }
    
#クラスのおわり}


mcrypt.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
    
    //準備
        require_once('mcrypt.class.php');
        $crypt=new Mcrypt('hogehoge');
    
    //暗号化するデータ
        $data='hoge';
    
    //暗号化
        $enc_data=$crypt->encrypt($data);
        $iv=$crypt->getIV();
    
    //復号化
        $dec_data=$crypt->decrypt($enc_data,$iv);


最終的に関数ふたつだけなのでわざわざクラス化するほどのものでもないのですが、これで纏まりました。
他にmcrypt_enc_get_supported_key_sizesなんかが欲しいと思えばラッピングしていくといいかもしれません。
公式がMcryptクラスを作成してくれればそれでおわりなんですがね。

 



2009/03/05 16:16 | Comments(0) | TrackBack() | PHP
PHP1-58:PHPでIMAP

3分LifeHacking:
Gmailが落ちたときに“アクセス”する方法を考える
http://www.itmedia.co.jp/bizid/articles/0902/25/news099.html

自力でIMAPすりゃいいじゃん?
ということで繋いでみた。

メールの送信はmail()やPear::Mail、そしてQdmailなんかでさくっと行うことができますが、受信についてはこれといったものがありません。

GmailはIMAPに対応しているので、PHPのIMAP関数でこりこり書いていくしかないようです。
IMAPを使用するので、最初にGmailの設定で「IMAPを許可」する必要があります。
http://ja.wikipedia.org/wiki/Internet_Message_Access_Protocol
http://jp.php.net/manual/ja/book.imap.php

gmail_imap.class.php 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
    //初期設定
    
class gMail implements Iterator{
    //メールサーバ情報
    protected $params= array(
         'host'     => 'imap.gmail.com'
        ,'port'     => 993
        ,'auth'     => true
        ,'username' =>''
        ,'password' =>''
        ,'mailbox'  =>''
    );
    
    //IMAPインスタンス
    protected $imap=array();
    protected $mailbox='';
    
    //受信メール
    private $loop=0;
    protected $mail_data=array();
    
    //コンストラクタ
    public function __construct($user,$pass){
        $this->params['username']=$user;
        $this->params['password']=$pass;
        
        //IMAP:mailboxの作成
        $this->mailbox
            ='{'.$this->params['host'].':'.$this->params['port'].'/ssl}INBOX';
        //インスタンス
        $this->imap=imap_open(
            $this->mailbox,$this->params['username'],$this->params['password']
        );
    }
    
    //デストラクタ
    public function __destruct(){
        imap_close($this->imap);
    }
    
    //全件を取得
    public function getAll(){
        $count=$this->count();
        for($i=1;$i<$count+1;$i++){
            $this->mail_data[]=$this->get($i);
        }
        return $this->mail_data;
    }
    
    //件数を取得
    public function count(){
        $this->count=imap_num_msg($this->imap);
        return $this->count;
    }
    
    //受信
    public function get($num){
            $this->tmp=false;
        //ヘッダ
            $this->tmp['head']=imap_headerinfo($this->imap,$num);
            $this->tmp['head']->uid=imap_uid($this->imap,$num);
        //本文
            $this->tmp['body']=imap_body($this->imap,$num);
        //デコード
            $tmp=$this->_decode($this->tmp);
        //必要なぶんだけ返却
            $ret['to']=$tmp['head']->toaddress;
            $ret['to_address']=$tmp['to_address'];
            $ret['from']=$tmp['from'];
            $ret['from_address']=$tmp['from_address'];
            $ret['date']=$tmp['head']->date;
            $ret['subject']=$tmp['subject_decode'];
            $ret['body_decode']=$tmp['body_decode'];
            return $ret;
    }
    
    //デコード
    private function _decode($mail_data){
        //件名
            $mail_data['subject_decode']
                =mb_decode_mimeheader($mail_data['head']->subject);
        //宛先
            if(isset($mail_data['head']->to[0]->personal)){
                $mail_data['to']=mb_decode_mimeheader(
                    $mail_data['head']->to[0]->personal
                );
            }else{
                $mail_data['to']=$mail_data['head']->toaddress;
            }
            $mail_data['to_address']
                =$mail_data['head']->to[0]->mailbox
                .'@'.$mail_data['head']->to[0]->host;
        //差出人
            if(isset($mail_data['head']->from[0]->personal)){
                $mail_data['from']=mb_decode_mimeheader(
                    $mail_data['head']->from[0]->personal
                );
            }else{
                $mail_data['from']=$mail_data['head']->fromaddress;
            }
            $mail_data['from_address']
                =$mail_data['head']->from[0]->mailbox
                .'@'.$mail_data['head']->from[0]->host;
            
        //本文
            if(isset($mail_data['body'])){
                $mail_data['body_decode']=mb_convert_encoding(
                    $mail_data['body'],'UTF-8','JIS'
                );
            }
            return $mail_data;
    }
    
    //イテレータ
    function current(){
        return $this->mail_data[$this->loop];
    }
    function next(){
        $this->loop++;
    }
    function key(){
        return $this->loop;
    }
    function rewind(){
        $this->loop=0;
    }
    function valid(){
        if(isset($this->mail_data[$this->loop])){
            return true;
        }else{
            return false;
        }
    }
    
#↓クラスのおわり
}

gmail_imap.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
    //初期設定
    header("Content-type: text/html; charset=utf-8");
    require_once('./gmail_imap.class.php');
    
    //ユーザ情報
    $mail_user='メアド';
    $mail_pass='パスワード';
    
    //アカウント
    $gmail = new gMail($mail_user,$mail_pass);
    
    //一件取得
    $ret=$gmail->get('8');
    
    //全件取得
    $ret=$gmail->getAll();
    foreach($ret as $key=>$val){
        var_dump($key,$val);
    }

何気にメール一覧の取得が難しいです。
imap_headersは、ヘッダ情報が文字列で返ってくるという謎仕様です。
string(83) " U       4)25-Feb-2009 =?ISO-2022-JP?B?GyRC =?ISO-2022-JP?B?MTUbJEI8f (10362 chars)"
こんなの送ってこられてもどうしようもないのですが…
他に全ヘッダを取得という関数が無いようなので、全メールについてimap_fetchheaderなりimap_headerinfoなりをせざるを得ないとかいうことになっています。
どうせなら一緒でいいやということでついでにimap_bodyも実行して全メール取得にしてしまいました。
これでメールの件数を取得、特定のメールを受信、全メール受信のスクリプトができました。

mb_detect_encodingがJISをまったくチェックしてくれないのでJISべた書きだったり、そのせいで標準以外のエンコードがされているメールが読めなかったり、そもそもマルチパート対策が全くなされていなかったりとメーラとしては弱いです。
またIMAPにはフォルダ移動や削除などサーバ側のメールを操作する機能もたくさんあるのですが、現在は受信トレイのメールを受信する以外の機能はありません。
そこらへんは必要になったら加えていけば便利になっていくことでしょう。

 

まあ根本的に外部メーラ使った方が遙かに手っ取り早いというのは秘密だ。



2009/03/02 16:06 | Comments(0) | TrackBack() | PHP
PHP1-57:PHPでQdsmtp

かつてPear::Mailを使用してGmailからメール送信というのを行いましたが、日本産ではないのでヘッダの日本語対応等少々面倒なところがあります。
世の中は広いもので、Pear要らずの日本語メール送信スクリプトを公開してくれている人がいます。

Qdsmtp 0.2.0a
http://hal456.net/qdsmtp/

準備は、qdsmtp.phpを適当なフォルダに置くだけです。
とりあえず普通にメール送信してみます。

decomail.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
    //require
        require_once('qdsmtp.php');
    
class Decomail{
    
    //送信メールの初期値
        protected $param = array(
             'host'=>'tls://smtp.gmail.com'
            ,'port'=> 465
            ,'protocol'=>'SMTP_AUTH'
            ,'user'=>''
            ,'pass' => ''
            ,'from'=>''
        );
    
    //Qdsmtp
        protected $smtp='';
    
    //コンストラクタ
        public function __construct($user,$pass,$from){
            //引数セット
                $this->param['user']=$user;
                $this->param['pass']=$pass;
                $this->param['from']=$from;
            //Qdsmtp
                $this->smtp = & new QdSmtp($this->param);
                $this->smtp->continueConnect(true);
        }
    
    //送信
        public function send($mailto,$subject,$body){
            //送信
                return $this->smtp -> mail(
                     $mailto
                    ,$subject
                    ,$body
                );
        }
    
#クラスのおわり
}

使用は普通にnewしてsendするだけ。

decomail.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
    //初期設定
        $user='ユーザ名';
        $pass='パスワード';
        $from='送信元メアド';
    
    //インクルード
        require_once('decomail.class.php');
        $smtp=new Decomail($user,$pass,$from);
    
    //送信
        $mailto='送信先メアド';
        $subject='件名';
        $body='本文';
        $ret=$smtp->send($mailto,$subject,$body);

Googleからあっさり送信できました。
あとは引数のチェックルーチンなんかを入れると完成です。

一見Pear::Mail版とほとんど変わりませんが、こちらはqdsmtp.phpを一本置いただけです。
Pearをインストールできない環境などでは重宝するでしょう。
また、ヘルプでは日本語が使用できないと書いてありますが、環境によっては通るみたいです。
http://hal456.net/qdsmtp/mail
とりあえず私の手元では何の問題もなく日本語メールが送信できました。何でしょう?

 



2009/02/27 15:58 | Comments(0) | TrackBack() | PHP
PHP1-56:PHPでオンライン辞書さらにつづき

前回のさらに続き?

SOAPとは、ネット越しに情報をやりとりする手段の一つです。
http://ja.wikipedia.org/wiki/SOAP_(%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB)
Webサービスのメイン手段として登場し、特徴としてはすべてをXMLでやりとりするため汎用化がしやすい(ということになっている)ことや、WSDLという設計図を利用して簡単にサービスが構築できる(ということになっている)ことがあります。
http://ja.wikipedia.org/wiki/WSDL

が、より簡単なXML-RPCや、もっと簡単にfile_get_contentsするだけで全てが終わるRESTなどが普及したため、あまり使われていません。
確かに出来ることはSOAPのほうが多いのですが、なにしろ面倒ですからな。
また、Yahooは最初からRESTしか無かったり、SOAPの主導権争いに嫌気がさしたGoogleの中の人がSOAP対応を打ち切るなど将来も微妙な感じです。

企業間通信やなどでは今でも使われているところはあるようですが、実際WebサービスとしてSOAPを提供しているところはググってもあまり出てきません。

イースト辞書WebサービスはSOAPにも対応しているのでそちらを使用してみましょう。
http://www.btonic.com/ws/index.html


WSDLに関しては、サーバ上のWSDLを直接参照することも出来ますが、まああまり迷惑をかけないよう拾ってきてローカルに保存しておきましょう。

WSDLファイルにはGetDiclistとかSearchDicItemといった要素が書かれていますが、これをSoapClient関数に読み込ませることによってSoapClient::GetDiclistやSoapClient::SearchDicItemといった関数を使えるようになります。
http://jp.php.net/manual/ja/book.soap.php
WSDLがWebサービスの設計図となっているのでこういう芸当が可能なのです。

というわけでとりあえずGetDiclistとSearchDicItemを作ってみます。

dict_soap.class.php 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?php
    
    //検索を行うクラス
    class Dict_Soap{
        
        //URL
            protected $url_service=
                'http://btonic.est.co.jp/Netdic/Netdicv10.asmx';
        //WSDLファイル
            //protected $file_wsdl=
                'http://btonic.est.co.jp/Netdic/Netdicv10.asmx?WSDL';
            protected $file_wsdl='Netdicv10.wsdl';
            
        //SOAPオブジェクト
            protected $soap='';
            
        //辞書リスト
            protected $diclist=array();
            
        //メソッド用パラメータ初期値
            //辞書リスト取得
            protected $param_GetDicList=array(
                'AuthTicket'=>''
            );
            //検索
            protected $param_SearchDicItem=array(
                 'AuthTicket'=>''
                ,'DicIDList'=>array(
                    '19553dab-aa51-43bb-9fb7-764153b647ac'    //EJDict英和辞典
                    //'8a68bb8a-16ee-4b51-afaa-74c277bb600a'//Edict和英辞典
                    //'39124c12-6244-4c65-94eb-8b18c20c2898'//Wikipedia
                )
                ,'QueryList'=>array(
                     'ScopeID'=>'ANYWHERE'
                    ,'MatchOption'=>'EXACT'
                    ,'MergeOption'=>'AND'
                )
                ,'ContentProfile'=>array(
                     'FormatType'=>'XHTML'
                    ,'ResourceOption'=>'URI'
                    ,'CharsetOption'=>'WINJPENV'
                )
                ,'SortOrderID'=>''
                ,'ItemStartIndex'=>'1'
                ,'ItemCount'=>'4'
                ,'CompleteItemCount'=>'2'
            );
        
        //コンストラクタ
            function __construct($AuthTicket=false){
                $this->soap = new SoapClient($this->file_wsdl);
                //チケットがあれば
                if($AuthTicket){
                    $this->$this->param_GetDicList['AuthTicket']=$AuthTicket;
                    $this->$this->param_SearchDicItem['AuthTicket']=$AuthTicket;
                }
            }
        
        //使用可能な辞書のリストを取得
        public function getDicList(){
            //取得
                $tmp=$this->soap->GetDiclist($this->param_GetDicList);
            //返り値のうち必要な部分のみ取得
                foreach($tmp->GetDicListResult->DicInfo as $key=>$val){
                    $this->diclist[$key]['DicID']=$val->DicID;
                    $this->diclist[$key]['FullName']=$val->FullName;
                    $this->diclist[$key]['Abbrev']=$val->Abbrev;
                }
            return $this->diclist;
        }
        
        //辞書を検索する
        public function searchDicItem($string){
            //検索語句セット
                $this->param_SearchDicItem['QueryList']['Words']=$string;
            //何故か返り値が常に見つからない
                $ret=$this->soap->SearchDicItem($this->param_SearchDicItem);
                return $ret;
        }
        
    #↓クラスのおわり
    }


できました。
一部分テキトーなので辞書IDなんかがべた書きになってますがまあ気にしない。
さて実行。

soap.php
1
2
3
4
5
6
7
8
9
10
11
<?php
    
    //インスタンス
        require_once('./dict_soap.class.php');
        $dictsoap=new Dict_Soap();
        
        
        //$ret=$dictsoap->getDicList();
        $ret=$dictsoap->searchDicItem('love');
        
        print("<html><pre>");var_dump($ret);die();


実行結果。

object(stdClass)#3 (2) {
  ["SearchDicItemResult"]=>
  int(0)
  ["ItemList"]=>
  object(stdClass)#4 (0) {
  }
}


…あれー?
SearchDicItemがうまく動いてくれません。
送信メッセージ形式は合っている筈なんだが…

getDicListは結果がきちんと返ってきます。
二重配列内の、WSDLでminOccurs="1"と定義されている部分、たとえば$param_SearchDicItem['QueryList']['MatchOption']あたりを削除してみると正しく不正になる(どころか、正しくないことにApache毎落ちる)のでパラメータの指定が間違っているということもないと思います。

うむ、よくわからん。

今回は原因特定しきれなかったので相手が悪いのだと勝手に納得しつつ終了。


2009/02/25 19:07 | Comments(0) | TrackBack() | PHP
PHP1-55:PHPでオンライン辞書つづき

前回の続き。

というわけでDICTプロトコルは全く役に立たないことがわかったので、日本語対応のオンライン辞書を探してみます。
ググると一杯出てきますな。
とりあえず「辞書 API」で真っ先に出てきたイースト辞書Webサービスを試してみることにします。
http://www.btonic.com/ws/index.html

イースト辞書Webサービスはイースト株式会社が作成している辞書Webサービスのテストサイトという位置づけのようです。
http://www.est.co.jp/

とりあえず簡単なほうのRESTで。
http://www.btonic.com/ws/NetDicv09RestSpec.htm
見ての通りHTTPリクエストを投げるだけという簡単設定。

これに限らず専用のプロトコルを使用するよりHTTPのほうが遙かにメジャーで簡単なので、専用プロトコルは今後どんどん滅びていくものと思われます。
もちろんセキュリティが必要だったり高度に専門性が必要だったりといった特別なものは別ですが。

作成方針としては、まず検索メソッドで該当語句の含まれる記事の一覧を取得し、その中から該当の記事のIDを探し出し内容取得メソッドで内容を取得する、という順番になります。

dict_east.class.php
ソースは本文長すぎとか意味不明なことを言われたので ファイルにしてあります。


Dict_East_Search::currentはforeachで配列を返す部分を実装しています。
ItemIDのままだとオブジェクトを返してしまい、asXMLだと現在地点である<ItemID>等のタグまでくっついてきてしまうので(string)で無理矢理文字列にしています。

Dict_East::searchメソッドは単純にsimplexml_load_string()でパースしています。
こちらは返り値の各項は値なので単純にsimplexml_load_stringを適用して問題ありません。

ところがDict_East::requestメソッドは返り値が以下のようにHTMLタグ込みで返ってきます。
 

<Head>
  <div class="NetDicHead" xml:space="preserve" xmlns="">
    <span class="NetDicHeadTitle">山口人生</span>
  </div>
</Head>


この場合、XMLパース関数はどれも癖があり、単純に<Head>タグ内の値だけを全取得したいだけという場合のパースが困難です。

simplexml_load_string()xml_parse_into_struct()を使用するとHTMLタグ単位で全てオブジェクトや配列に分解されてしまい、えらいことになってしまいます。
DOMDocumentクラスのgetElementsByTagNameあたりを使用するとHTMLタグが全て削除されてしまいます。
XMLReaderは一行毎に読み込んでは処理するという形式のため実装が面倒になってやめました。

結局自力でのパースは諦め、simplexml_load_string()後にxpathでノードを指定してasXMLでXMLだということにして返すようにしました。
このときにまたネームスペースが存在する場合は単純指定では取得できないとか一悶着あったが、最終的にはどうにか成功しました。

使用する場合は簡単にこんなふうに使います。

dict_east.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
    
    //インスタンス
        require_once('./dict_east.class.php');
        $dicteast=new Dict_East();
        
        $ret=array();
        $dicteast_search_array=array();
    
    //オプション設定
        //$dicteast->setDictionary('EJdict');
        //$dicteast->setSearchScope('ANYWHERE','EXACT');
        
    //検索メソッド
        $dicteast_search=$dicteast->search('');
        $count=$dicteast_search->getCount();
        foreach($dicteast_search as $val){
            $dicteast_search_array[]=$val;
        }
        
    //取得メソッド
        $ret=$dicteast->get($dicteast_search_array[0]['itemID']);
        
print("<pre>");var_dump($dicteast_search_array,$ret);die();

まあこんなかんじでネット辞書検索APIのクライアントを作成することが出来ました。
後は適当にフロントを実装すれば簡単に単語翻訳なんかを作成することができます。
 

 



2009/02/23 14:13 | Comments(0) | TrackBack() | PHP

<<前のページ | HOME | 次のページ>>
忍者ブログ[PR]