忍者ブログ
[PR]
×

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



2026/04/30 19:22 |
PHP1-32:PHPでカレンダー

Pear::Calendar 0.5.3 (beta)
http://pear.php.net/package/Calendar/

たとえばこのブログでは右側に表示されているカレンダー、そういうのを簡単に作成することの出来るパッケージです。
ブログの場合は日付毎の記事等と連動しているので最初からブログシステムに組み込まれている場合がほとんどで、特に自作する必要はありません。
しかし自力で実装を行おうと思った場合はそれなりに面倒です。
そんな場合にPear::Calenderが便利だよ、ということなのですが、これがまたはっきりいってものすごく使いづらい。
クラスやファイルが多くてインクルードが面倒だったり、どのファイルのどのクラスがどの機能を持っているのかが分かり難かったり、取得したところで結局自分でタグ書かないといけなかったり。
$calender->getMonthTable('2008','9')みたいなことができず、Calendar_Month_Weekdaysオブジェクトをbuildして、isFirst、isLastでチェックしつつfetchしなけりゃいけないので自作するのとあまり手間が変わらなかったりします。
さらに何故かCalendarクラスの初期値が'2001-01-01 00:00:00'とかになっているので(普通nowだろ)どうにかします。

というわけでテーブル作成部分などを例によってラッパークラスに押し込みます。
汎用性?窓から投げ捨てろ。
フツーにstrtotimeやらgetdateやら使用しているせいでPear::Date涙目なのですがそれもまた気にしない。

calender.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
138
<?php
    //初期設定
    require_once(PEAR_DIR.'Calendar/Calendar.php');
    require_once(PEAR_DIR.'Calendar/Year.php');
    require_once(PEAR_DIR.'Calendar/Month.php');
    require_once(PEAR_DIR.'Calendar/Week.php');
    require_once(PEAR_DIR.'Calendar/Day.php');
    require_once(PEAR_DIR.'Calendar/Hour.php');
    require_once(PEAR_DIR.'Calendar/Minute.php');
    require_once(PEAR_DIR.'Calendar/Second.php');
    require_once(PEAR_DIR.'Calendar/Month/Weekdays.php');
    require_once(PEAR_DIR.'Calendar/Month/Weeks.php');
    require_once(PEAR_DIR.'Calendar/Table/Helper.php');
    
    class pearCalenderModel {
        //メンバ変数
        private $calender = array();
        private $date_array = array();
        private $table='';
        private $firstday=0;
        
        //コンストラクタ
        //引数は$date:'Y-m-d h:i:s(strtotimeが理解できる形式)'
        //$firstday:最初を何曜日にするか0:Sun,1:Mon,…,6:Sat
        public function __construct($date='',$firstday=null){
            $this->setDate($date);
            $this->calender = new Calendar(
                $this->date_array['year'],
                $this->date_array['mon'],
                $this->date_array['mday'],
                $this->date_array['hours'],
                $this->date_array['minutes'],
                $this->date_array['seconds']
            );
            if($firstday!==null){$this->firstday = (int)$firstday;}
        }
    
        //月別カレンダーを作成
        public function createMonthTable(){
            $this->_tableHelper();
            $Month = new Calendar_Month_Weekdays(
                $this->date_array['year'],
                $this->date_array['mon'],
                $this->firstday);
            $Month->build();
            
            $ret= '<table border="1"><tr>';
            $DaysOfWeek=$this->table->getDaysOfWeek();
            foreach($DaysOfWeek as $val){
                switch($val){
                    case 0:$ret.= '<th>Sun</th>';break;
                    case 1:$ret.= '<th>Mon</th>';break;
                    case 2:$ret.= '<th>Tue</th>';break;
                    case 3:$ret.= '<th>Wed</th>';break;
                    case 4:$ret.= '<th>Thu</th>';break;
                    case 5:$ret.= '<th>Fri</th>';break;
                    case 6:$ret.= '<th>Sat</th>';break;
                }
            }
            $ret.='</tr>';
 
            while ($Day = $Month->fetch()) {
                if ($Day->isFirst()) {
                    $ret.= "<tr>";
                }
                if ($Day->isEmpty()) {
                    $ret.= "<td>&nbsp;</td>";
                }else{
                    $ret.= '<td>'.$Day->thisDay()."</td>";
                }
                if($Day->isLast()){
                    $ret.= "</tr>";
                }
            }
            $ret.= "</table>";
            return $ret;
        }
    
        //週間カレンダーを作成
        public function createWeekTable(){
            $this->_tableHelper();
            
            $Week = new Calendar_Week(
                $this->date_array['year'],
                $this->date_array['mon'],
                $this->date_array['mday'],
                $this->firstday);
            $Week->build();
            
            $ret= '<table border="1"><tr>';
            $DaysOfWeek=$this->table->getDaysOfWeek();
            foreach($DaysOfWeek as $val){
                switch($val){
                    case 0:$ret.= '<th>Sun</th>';break;
                    case 1:$ret.= '<th>Mon</th>';break;
                    case 2:$ret.= '<th>Tue</th>';break;
                    case 3:$ret.= '<th>Wed</th>';break;
                    case 4:$ret.= '<th>Thu</th>';break;
                    case 5:$ret.= '<th>Fri</th>';break;
                    case 6:$ret.= '<th>Sat</th>';break;
                }
            }
            $ret.='</tr><tr>';
            while ($Day = $Week->fetch()) {
                $ret.= '<td>'.$Day->thisMonth().'/'.$Day->thisDay()."</td>";
            }
            $ret.= '</tr></table>';
            return $ret;
        }
    
        //日付を設定
        public function setDate($date=''){
            if($date){
                $this->date_array=getdate(strtotime($date));
                if($this->date_array==false){
                    $this->date_array=getdate();
                }
            }else{
                $this->date_array=getdate();
            }
        }
    
        //日付を取得
        public function getDate(){
            return $this->date_array;
        }
        
        //Calendar_Table_Helperインスタンスを作成
        private function _tableHelper(){
            if($this->table){return false;}
            
            $this->table = new Calendar_Table_Helper
                ($this->calender,$this->firstday);
            return true;
        }
    
    #↓クラスのおわり
    }


これだけ書いて実装できたのはcreateMonthTable()およびcreateWeekTable()だけです。
それぞれ任意の日付が含まれる月間カレンダー、週間カレンダーを作成します。
以下のように使用します。

calender.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
    #初期設定
    header("Content-type: text/html; charset=utf-8");
    require_once('./calender.class.php');
    
    $calender = new pearCalenderModel();
    //$calender = new pearCalenderModel('2008-09-25 00:00:00',0);
    
    $cal_now=$calender->getDate();
    $cal_now=$cal_now['year'].''.$cal_now['mon'].'';
    
    $cal_m=$calender->createMonthTable();
    $cal_w=$calender->createWeekTable();
    
    print($cal_now.'<br />'.$cal_m.'<br />'.$cal_w);


new時に引数としてY-m-d h:i:s形式の日付(省略するとnow)、およびカレンダーの曜日を何曜日から始めるかの二つを渡します。
最初はcreate時に$firstdayを与えるつもりだったのですが、Calender::firstdayは中でfirstdayをdefineしてやがる。
カレンダーを作成するたびにクラスをnewするのも馬鹿みたいなので適当に実装しました。
変更するならコンストラクタでやってることを各メソッドに持って行けばいいです。

最初にrequireしているファイルの半分も使っていませんが、だいたいWebで必要なカレンダーなんてこの程度しかないような。
何かほしいものがあるならばおいおい追加していくといいかもしれませんし一から作った方が早いかもしれません。
どうせPHPで行うなら単なるカレンダーではなく、DBと連携して予定表などを取得してみるのもいいかもしれません。
DBなどと連動しない場合はわざわざサーバに負担をかける必要もないので、YUIカレンダー等のJavaScriptベースのカレンダーを使用した方がいいでしょう。

 

PR


2008/10/09 11:18 | Comments(0) | TrackBack() | PHP
PHP1-31:PHPでソケット接続
PHPにはfile_get_contentsやらftp_getといった超便利関数が揃っているので、リモートファイルへのアクセスが非常に簡単です。
他言語では入出力ストリームを作成して云々、ファイルポインタを作成して云々、などと一手間かけないといけないのですが、PHPだと一瞬で終わってしまうのが楽なところでもありいい加減なところでもあります。
そんなわけで普通にWebアプリを作成する上では全く使用することはないのですが、意味もなく直接関数を書いて接続することも出来ます。
とりあえずソケット接続してみましょう。

Socket以外にもCurlSOAPやその他諸々のネットワーク系関数がたくさんあるのですが正直さっぱりわかりません。
いったい何がどう違うんだ。

socket.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
<?php
    //送信内容
    $sock_data_array['id']='123';
    $sock_data_array['number']='456';
    
    //ソケット送信先
    $sock_server='www.example.com';
    $sock_port='80';
    $sock_script='index.php';
    
    //送信データ作成
    $sock_data=http_build_query($sock_data_array,"","&");
    $sock_server_ip=gethostbyname($sock_server);
    
    $send  = "POST /".$sock_script." HTTP/1.0\n";
    $send .= "Content-type: application/x-www-form-urlencoded\n";
    $send .= "Content-Length: ".strlen($sock_data)."\n\n";
    $send .= $sock_data."\n\n";
    
    //ソケット作成
    $sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
    
    //ソケット接続先
    socket_connect($sock,$sock_server_ip,$sock_port);
    
    //データ送信
    socket_write($sock,$send,strlen($send));
    
    //返り値取得
    $ret='';
    while($sock_res=socket_read($sock,4096)){
        $ret.=$sock_res;
    }
    
    //ソケット閉じる
    socket_close($sock);
    
    var_dump($ret);
    
    
    #file_get_contentsで同じことをやってみる
    
    //URL
    $con_url='http://'.$sock_server.'/'.$sock_script;
    
    //送信データ作成
    $send = "Content-type: application/x-www-form-urlencoded\n";
    $send.= "Content-Length: ".strlen($sock_data)."\n\n";
    
    $con_context = array(
        "http" => array(
            "method"  => "POST",
            "header"  => $send,
            "content" => $sock_data
        )
    );
    $con_stream=stream_context_create($con_context);
    
    //データ取得
    $ret=file_get_contents($con_url, false, $con_stream);
    
    var_dump($ret);


$sock_data_arrayに登録した内容がPOSTで送信されます。

$sock_serverにサーバのアドレス、$sock_scriptにパスを含めたファイル名を記入します。
$sock_scriptに対応するファイルには、適当にvar_dump($_REQUEST);でも入れておきましょう。
$sock_portはhttpである限り普通は80です。

socketではヘッダを含めたレスポンス全体が、file_get_contentsではsocketからヘッダを除いた内容が取得できたはずです。

上記内容は単にfile_get_contentsが内部でやっているだろう動作を適当にソケット関数を使ってやってみたというだけで、こういった内容ならわざわざソケット関数を使用する必要もありません。
TCP以外のプロトコルで通信したいとか、任意のヘッダを送信したいといった場合に役に立つかもしれませんししないかもしれません。
あまりに本格的な通信プログラムをPHPで作るのもどうかと思いますし、それ以前によくわかりませんし。
 
どうでもいいが↓でエラーになるのは何故ですか。

socket_get_option($sock,getprotobyname('tcp'),SO_LINGER);
Warning: socket_get_option() [function.socket-get-option]: unable to retrieve socket option [0]: 無効な引数が提供されました。

無効とか言われましても。



2008/10/03 20:07 | Comments(0) | TrackBack() | PHP
PHP1-30:GMailから簡単にメール
さて先日メールを送信するクラスを作成しましたが、使い回しとかしないので一回だけでいい、とかいうのでしたらもっと簡単に記述することが出来ます。
以下をコピペしてユーザIDとパスワードを入れるだけ。
簡単ですね。

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
    //Pear::Mail
    require_once('Pear/Mail/Mail.php');
    
    //パラメータ
    $params= array(
        'host'     => 'tls://smtp.gmail.com',
        'port'     => 465,
        'auth'     => true,
        'debug'    => false,
        'username' =>'名前',
        'password' =>'パスワード'
    );
    $headers = array(
        'To'=>'宛先',
        'From'=>'送信元',
        'Subject'=>'件名'
    );
    $mailto='宛先';
    $body = '本文';
    
    //エンコード
    $tmp=mb_internal_encoding();
    mb_internal_encoding('UTF-8');
    $headers['Subject']
    =mb_encode_mimeheader($subject,'ISO-2022-JP');
    $body=mb_convert_encoding
    ($body,'ISO-2022-JP', mb_detect_encoding($body));
    mb_internal_encoding($tmp);
    
    //送信
    $smtp=Mail::factory('smtp', $this->params);
    $smtp->send($this->mailto, $this->headers, $this->body);


UTF-8以外だと文字化けするので、その場合はmb_internal_encodingをファイルの文字コードに合わせてください。
まあ直接書いた方が手っ取り早いというのはクラス全般に言えることですが、そのぶんクラスは一回書いたらコピペいらずでincludeするだけで使い回せるというのがポイントです。

しかしCAのサーバ証明書とかインストールした覚えがないのだが、これはきちんと動いているのだろうか。
よくわからない。



2008/10/02 20:29 | Comments(0) | TrackBack() | PHP
PHP1-29:WYSIWYGなHTMLエディタ

FCKeditor 2.6.3
http://www.fckeditor.net/download

前にWYSIWYGなHTMLエディタを作成しましたが、見た目はともかくタグは実際に自力で打ち込む必要があります。
HTMLタグの入力支援を行うプラグインがありますので使用してみましょう。

今回使用するのは、この忍者BLOGでも使用されているFCKeditorです。
最初から日本語をはじめ多くの言語に対応しています。
PHPからでもJavaScriptからでも、他にもASPやPerlからでも使用できるというマルチな対応っぷりで、さらに_samples/default.htmlを表示するだけで動作確認が出来てしまうという至れり尽くせり仕様です。
正直ソース見れば全部書いてあるのでわざわざ解説するようなこともないのですが、まあとりあえず導入してみます。
PHPに対応しているといっても、PHPでできることは既に用意されているプロパティを変更することです。
自作のメニューを使用したいといった場合は結局JavaScriptを記述する必要があります。

準備としてはまずディレクトリの中身を丸ごとfckeditorフォルダに放り込み、PHPでそこにパスを張って適当にメソッドを実行します。

index.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
<?php
    //初期設定
    require_once('fckeditor/fckeditor.php');
    $fck = new FCKeditor('fieldname');
    
    //fckeditorの入っているパスを指定
    $fck->BasePath    =dirname($_SERVER['PHP_SELF']).'/fckeditor/';
    
    //適当に設定
    $fck->ToolbarSet = 'Default';
    //$fck->Width='100%';
    //$fck->Height='95%';
    $fck->Value='初期値';
    $fck->Config['SkinPath']=$fck->BasePath.'editor/skins/office2003/';
    $fck->Config['ForcePasteAsPlainText']=true;
    
    //HTML文作成
    $fck_form=$fck->CreateHtml();
?>
 
<html>
<head></head>
<body>
    <form method="POST" action="post.php">
        <?php print($fck_form); ?>
        <input type="submit" name="a" value="送信">
    </form>
    
</body>
</html>

post.php
1
2
<?php
    var_dump($_REQUEST);


PHPで用意されているメンバ変数は以下の6つです。
$InstanceName:<input name="**">に入る値)
$BasePath:DocumentRootからのパスを指定
$Width、$Height:Textareaの幅、高さ
$ToolbarSet:ツールバーセット、最初からDefault,Basic,Customの3種類が用意されている
$Value:Textareaの初期値

それ以外のプロパティを変更したい場合、
$fck->Config['StartupFocus']=true;
という風にプロパティ名を直接指定して変更します。
具体的な設定項目は非常に多いので直接fckconfig.jsを見てください。

これでメニューで編集できる、WYSIWYGなHTMLエディタがとても簡単に作成できました。


さて注意しないといけないのは、このフォームで送られてきたデータは別に必ずしもHTMLとして完成されているわけではないということです。
エディタで作成していれば全てのタグがきちんと閉じられているはずですが、ソース表示モードで編集した場合や直接POSTデータをいじられた場合など、不完全なデータがやってくる可能性は大いにあります。
タグ閉じ処理等はデータを受け取った側で行う必要があります。

…のですが、この忍者BLOGですらその処理を行っていません。
ソース表示モードで<b>とか打っちゃって送信すると、以降のエントリにもそのまま適用されてしまいます。

また危険なタグやコードを書いても普通に送られてしまうので、そこらへんのバリデートは受け側で行わなければなりません。
それもやってないので普通に<script>alert();</script>が通ってしまうんだが。
いとも簡単にブラクラ仕掛けられるのですが大丈夫か忍者BLOG。

結局POSTデータのチェックは手動で行わねばならないようです。
そっち方面の注意書きや対処法が載っているサイトって見たこと無いのですが大丈夫か。
まあ、そこらへんはエントリ作成者の責任で行うべきといわれればそうかもしれませんが。
というわけで、コメント欄等の不特定多数が投稿できる部分には、FCKeditorを使用してはいけません。 



2008/09/30 11:24 | Comments(0) | TrackBack() | PHP
PHP1-28:XAMPPでメール

XAMPP環境でいきなり
mail($mail_to,$mail_subject,$mail_message,$mail_headers);
と書いてもメールを送信することは出来ません。
以下のようなエラーが返ってきます。

Warning: mail() [function.mail]: Failed to connect to mailserver at "localhost" port 25

PHPのメール関数は、自力でメールを送信するわけではなくSMTPサーバというメール処理プログラムに処理を預けています。
OutlookExpressやBeckyといったWindows用のメーラは、プロバイダにあるSMTPサーバに接続してメールの送受信を行っていますが、PHPのメール関連関数も同じです。
要するに、そこらへんの設定を行わなければなりません。

XAMPPには最初からMercuryというSMTPサーバが付属していますが、デフォルトでは無効になっています。
設定が甘いと不正メール中継の踏み台にされたりするからです。
また、MercuryはHTTPサーバ機能も持っているためApacheと干渉したりするので面倒な部分もあります。
OutlookExpressのように、手っ取り早く外部SMTPサーバを使って解決してしまいましょう。
今回対象とするサーバはGoogleGmailです。
GmailはSMTP_AuthおよびSSLが必須でありセキュリティ的にも丈夫です。

さて、そうすると本来ならfsockopenやらを使用して外部SMTPサーバに接続してにょろにょろといったことを行わないといけないのですが、そこらへんを自力でどうにかするのはとても大変です。
単純な送信程度ならまだしも、うっかり添付ファイルを送信したいなあとか考えてしまうと速攻行き詰まります。
メールのフォーマットは歴史的経緯もあり非常に複雑怪奇なものとなっており、自力での実装は正直無理だろこれ。
http://www.atmarkit.co.jp/fnetwork/rensai/netpro03/netpro01.html
http://www.atmarkit.co.jp/fnetwork/rensai/netpro04/netpro01.html
ましてやSSLやらSMTP-Authやらが入ってきた日にはもう何が何だか。

というわけで例によってPearを利用してGmailに接続してみます。

Mail 1.2.0b1 (beta)
http://pear.php.net/package/Mail
Net_SMTP 1.3.1 (stable)
http://pear.php.net/package/Net_SMTP
Net_Socket 1.0.9 (stable)
http://pear.php.net/package/Net_Socket/

Pear::MailをMailフォルダに、Net_SMTPとNet_SocketをNetフォルダに格納します。
そしてさっそくラッパクラス。
 
gmail.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
<?php
    //初期設定
    define('PEAR_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/');
    ini_set('include_path',PEAR_DIR.PATH_SEPARATOR.ini_get('include_path'));
    require_once(PEAR_DIR.'Mail/Mail.php');
    
    class pearGMailModel{
        //メンバ変数
        private $params= array(
            'host'     => 'tls://smtp.gmail.com',
            'port'     => 465,
            'auth'     => true,
            'debug'    => false,
            'username' =>'',
            'password' =>''
        );
        private $headers = array(
            'To'=>'',
            'From'=>'',
            'Subject'=>''
        );
        private $mailto='';
        private $body = '';
        
        //コンストラクタ
        public function __construct($debug=null){
            mb_internal_encoding('UTF-8');
            if($debug=='debug'){$this->params['debug']=true;}
        }
        
        //ユーザ情報
        public function setUser($user,$pass){
            $this->params['username']=$user;
            $this->headers['From']=$user;
            $this->params['password']=$pass;
        }
        
        //メール内容
        public function setMail($subject,$body){
            //ヘッダはISO-2022-JP+mimeエンコード
            $this->headers['Subject']
            =mb_encode_mimeheader($subject,'ISO-2022-JP');
            //本文はISO-2022-JP
            $this->body=mb_convert_encoding
            ($body,'ISO-2022-JP', mb_detect_encoding($body));
        }
        
        //宛先
        public function setAddress($mailto){
            $this->mailto=$mailto;
            $this->headers['To']=$mailto;
        }
        
        //送信
        public function send(){
            if($this->_checkParam()===false){return false;}
            $smtp=Mail::factory('smtp', $this->params);
            return $smtp->send($this->mailto, $this->headers, $this->body);
        }
        
        //パラメータチェック
        public function _checkParam(){
            if(!$this->mailto 
                || !$this->body
                || !$this->params['username']
                || !$this->params['password']
                || !$this->headers['To']
                || !$this->headers['From']
                || !$this->headers['Subject']
            ){return false;}
            return true;
        }
        
    #↓クラスのおわり
    }

mb_encode_mimeheaderは恐ろしいことに文字コードを勝手に変更してしまいます。
変換後の文字コードはmb_encode_mimeheaderの2番目の引数が使われますが、変換元の文字コードは指定することが出来ず、mb_internal_encodingで指定したものが使われます。
ここらへんがmb_encode_mimeheaderに関する誤解と混乱の元となっているのですがまあいいや。

今回は直接mb_internal_encodingをコンストラクタで指定しています。
本当に正確を期するならmb_internal_encodingもmb_encode_mimeheaderもsendメソッド内で行い、最後はmb_internal_encodingを元に戻すべきなのですが、これもまあいいや。
基本的にUTF-8で統一していれば問題はありません。

使用する場合は以下のように。

gmail.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
    //初期設定
    header("Content-type: text/html; charset=utf-8");
    require_once('./gmail.class.php');
    
    //ユーザ情報
    $mail_user='ユーザ名';
    $mail_pass='パスワード';
    
    //メールの内容
    $mail_to='宛先';
    $mail_subject='件名';
    $mail_body='本文';
    
    //送信
    $gmail = new pearGMailModel('debug');
    $gmail->setUser($mail_user,$mail_pass);
    $gmail->setAddress($mail_to);
    $gmail->setMail($mail_subject,$mail_body);
    
    $ret=$gmail->send();

以上で自分のGMailアカウントからメールが送信されたはずです。
めでたし。


2008/09/29 19:40 | Comments(1) | TrackBack() | PHP
PHP1-27:QRコードを作ってみる

QRcode image PHP scripts  version 0.50g
http://www.swetake.com/

最近ではQRコードを読み取れない携帯はない、くらいに浸透したQRコードですが、個人でも作成、使用できます。
例によってQRコード作成ライブラリを公開してくれている人がいるので使ってみましょう。

使い方は死ぬほど簡単です。
qr_img.phpの設定項目のうち、$path、$image_pathをそれぞれdataフォルダ、imageフォルダのある場所に指定し、あとはブラウザでアクセスするだけです。
qr_img.php?d='test'
とすると、それだけで画像が表示されます。

エラーになった場合、$pathおよび$image_pathを設定しましょう。
また、GDを使用していますので、imagecopyresized()等が使えるか確認してみましょう。

さて本プログラム、これだけで完成しているのですが、ひとつ問題があります。
引数が全てリクエスト渡しとなっています。
そのせいでincludeが出来ず、またアクセス制御を行っていないので外部から簡単に利用されてしまいます。
勝手にクラス化してincludeできるようにしてみました。
出力イメージはこのようになります

ソースのダウンロードは↓こちらから。
qr_img.class.php

700行ある長大なファイルなので、さすがにいつものように掲載は出来ません。
ダウンロードしてみてください。

本当にただただ単にclassで括っただけという捻りの無さ過ぎる内容ですが、とりあえずクラス化されました。
使用時はいつものようにインスタンス化して適当にパラメータを突っ込んで最後にprintQRCodeすれば表示されます。

qr.php

<?php
    require_once('qr_img.class.php');
    $qr=new QR_IMG();

    $qr->set_qrcode_data_string('sample');
    $qr->set_qrcode_module_size(4);
    $qr->set_qrcode_image_type('jpg');
    $qr->set_qrcode_error_correct(1);
   
    $qr->printQRCode();

    //$image=$qr->getQRCode();
    //imagejpeg($image);

/*
    $ret=$qr->saveQRCode('image.jpg');
?>
    <img src="image.jpg" alt="QRコードサンプル" />
*/


printQRCodeで画像として出力、getQRCodeでGDオブジェクトとして取得、saveQRCodeでファイルに保存出来ます。



と、せっかくここまで作成したところでQRコードクラスライブラリの存在に気付いて俺涙目。



QRコードは(株)デンソーウェーブの登録商標です。

本品のついてのあらゆる権利はY.Swetake氏が所有しますが、本品についての質問、要望等をY.Swetake氏に対して行わないでください。



2008/09/26 21:54 | Comments(0) | TrackBack() | PHP
PHP1-26:バーコードを作ってみる

Image_Barcode 1.1.0 (stable)
http://pear.php.net/package/Image_Barcode

バーコードは何気に個人でも発行することが出来ます。

JAN企業コード(JANメーカーコード)利用の手引き
http://www.amazon.co.jp/dp/B000VPKMAW

まあ本格的にバーコードを利用しようとしている人がここを参考になんて間違ってもしないでしょうけれど、非常に簡単にバーコードを作成できるライブラリ、Image_Barcodeを使ってバーコードを作成してみます。
ちなみに日本で使用されているJANコードは、EANコードと同じものです。

Code39、Code128、EAN13、INT25、PostNet、UPCAと各種バーコードに対応していますが、Code128以外はintしか扱えません。
下手にstringとかを入れるとエラーが山ほど帰ってきてしまいますので、ラッパークラスでどうにかします。
ついでにエラーになりそうなところを片端からチェックしておきます。
おかげでなにやら無駄に長くなっていますが、特にエラーのこととかを考える必要がないのであれば直接呼んだ方が早いです。

barcode.class.php 

<?php
    define('CURRENT_DIR',getcwd().'/');
    define('PEAR_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/');
    ini_set('include_path',PEAR_DIR.PATH_SEPARATOR.ini_get('include_path'));
    require_once(PEAR_DIR.'Image/Barcode.php');
   
    class pearBarcodeModel {
       
        //メンバ変数
        private $barcode='';
        private $string='';
        private $code='ean13';
        private $ext='jpg';
       
        //コンストラクタ
        public function __construct(){
            $this->barcode=new Image_Barcode();
        }
       
        //文字セット
        public function setString($str){
            $this->string=$str;
        }
        //バーコード体系セット
        public function setCode($str){
            if(stripos($str,'39')!==false){
                $this->code='Code39';
            }else if(stripos($str,'128')!==false){
                $this->code='code128';
            }else if(stripos($str,'int')!==false){
                $this->code='int25';
            }else if(stripos($str,'post')!==false){
                $this->code='postnet';
            }else if(stripos($str,'up')!==false){
                $this->code='upca';
            }else{
                $this->code='ean13';
            }
        }
       
        //拡張子セット
        public function setExt($str){
            if(stripos($str,'j')===0){
                $this->ext='jpg';
            }else if(stripos($str,'g')===0){
                $this->ext='gif';
            }else{
                $this->ext='png';
            }
        }
       
        //表示
        public function printBarcode(){
            if($this->_checkArg()==false){return false;}
            $ret=$this->barcode->draw($this->string,$this->code,$this->ext);
        }
       
        //ファイルに保存
        public function saveBarcode($filename){
            if(!$filename || ($image=$this->getBarcode())===false){return false;}
           
            switch ($this->ext) {
                case 'gif':
                    $ret=imagegif($image,$filename);
                    imagedestroy($image);
                    break;
                case 'jpg':
                    $ret=imagejpeg($image,$filename);
                    imagedestroy($image);
                    break;
                default:
                    $ret=imagepng($image,$filename);
                    imagedestroy($image);
                    break;
            }
            return $ret;
        }
       
        //GDオブジェクトとして取得
        public function getBarcode(){
            if($this->_checkArg()==false){return false;}
            return $this->barcode->draw($this->string,$this->code,$this->ext,false);
        }
   
        //引数チェック
        private function _checkArg(){
            //code128以外は数値のみ
            if($this->code!='code128' && preg_match('/^[0-9]+$/',$this->string)==0){
                return false;
            }
            //upcaは数値12桁
            if($this->code=='upca' && strlen($this->string)!=12){
                return false;
            }
            return true;
        }
       
    #↓クラスのおわり
    }

barcode.php

<?php
    require_once('barcode.class.php');
    $barcode=new pearBarcodeModel();
   
    $barcode->setString('123456789');
    $barcode->setCode('128');
    $barcode->setExt('jpg');
   
    $ret=$barcode->printBarcode();

/*
    $ret=$barcode->saveBarcode('image.jpg');
?>
    <img src="image.jpg" alt="バーコードサンプル" />
*/


printBarcodeを呼べばそのまま画像ファイルとして出力します。
saveBarcodeでファイルとして保存されます。
getBarcodeでGDオブジェクトとして取得できるので、取得したバーコードを更に変更を加える場合はこちらを使用します。

このクラス最大の問題点は、使用者がいないということでしょう。



2008/09/26 17:10 | Comments(0) | TrackBack() | PHP
ひとつ気付いたんだが

忍者BLOG、
エントリ本文はしっかりタグ閉じ処理を行っているのに、
タイトルでは行っていません。

タイトルに<strike>とか書いてみるとアウチなことに。



と思ったら本文でも行っていませんでした。

通常時にタグを打つと自動で実体参照に変換されますが、
ソース表示状態で<b>とか打つとやっぱりそのまんま。


仕様的にどうなんだろう。


ていうか、
<img src="javascript:alert('hello');">
<BR style=left:expression(eval('document.location="http://www.google.co.jp/";'))>
とか書いちゃうとえらいことになるんですが。

普通に<script>alert();</script>で通るし。大丈夫なのか?
 



2008/09/24 13:28 | Comments(0) | TrackBack() | その他
PHP1-25:HTML_Table

HTML_Table 1.8.2 (stable)
http://pear.php.net/package/HTML_Table

HTMLでテーブルを作成するのはとても面倒です。
普通に一覧を作成するだけならいいのですが、たとえば列を追加したくなった場合など、おそろしい労力が必要です。
PHPならforeachで簡単ですが、colspanやrowspanを使用した表を作成したい場合はこれまた簡単にはできません。

そんなわけでセルや行や列を直接指定して内容を指定できるHTML_Tableを使ってみましょう。
なんといっても便利なのは、setContents(30,6,'中身')と直接場所を簡単に指定できるところです。
PHP側に直接タグを書かなくても、foreachで突っ込めばいいので簡単ですし、メンテナンス性も向上します。

そんなに便利ならもっと普及していいのではと思うのですが、このクラス、メソッドを小分けしすぎて逆に使いづらかったり、またcolspanとかの属性とかは結局直接指定しなければならなかったりするので、もう少しどうにかします。

table.class.php 

<?php
    //初期設定
    define('PEAR_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/');
    ini_set('include_path',PEAR_DIR.PATH_SEPARATOR.ini_get('include_path'));
    require_once(PEAR_DIR.'HTML/Table.php');
   
    class pearHtmlTableModel {
        //メンバ変数
        private $table ='';
        private $attribute =array();
       
        //コンストラクタ
        public function __construct($border=0,$class_table='table'){
            $this->table=new HTML_Table(array('border'=>$border,'class'=>$class_table));
            $this->table->setAutoGrow(TRUE);
        }
       
        //テーブル全体の指定
        public function setTableContents($caption){
            if($caption!==""){$this->table->setCaption($caption);}
        }
       
        //見出し
        public function setTableHeaders($type,$header_array){
            if($type=='col'){
                foreach($header_array as $key=>$val){
                    $this->table->setCellContents(0,$key,$val);
                }
                $this->table->setRowType(0,'th');
            }else if($type=='row'){
                foreach($header_array as $key=>$val){
                    $this->table->setCellContents($key,0,$val);
                }
                $this->table->setColType(0,'th');
            }
        }
       
        //セルの内容と属性セット
        public function setCellContents($row,$col,$contents='',$class_td='',$rowspan='',$colspan=''){
            if(is_null($col) || is_null($row)){return false;}
            if($contents){
                $this->table->setCellContents($row,$col,$contents);
            }
            $attribute_array=array();
            if($class_td){$attribute_array['class']=$class_td;}
            if($rowspan){$attribute_array['rowspan']=$rowspan;}
            if($colspan){$attribute_array['colspan']=$colspan;}
            if($attribute_array){$this->table->setCellAttributes($row,$col,$attribute_array);}
        }
       
        //列の内容と属性セット
        public function setColContents($col,$contents='',$class_td='',$rowspan='',$colspan=''){
            if(is_null($col)){return false;}
            if($contents){
                for($i=0;$i<$this->table->getRowCount();$i++){
                    $this->table->setCellContents($i,$col,$contents);
                }
            }
            $attribute_array=array();
            if($class_td){$attribute_array['class']=$class_td;}
            if($rowspan){$attribute_array['rowspan']=$rowspan;}
            if($colspan){$attribute_array['colspan']=$colspan;}
            if($attribute_array){$this->table->updateColAttributes($col,$attribute_array);}
        }
       
        //行の内容と属性セット
        public function setRowContents($row,$contents='',$class_td='',$rowspan='',$colspan=''){
            if(is_null($row)){return false;}
            if($contents){
                for($i=0;$i<$this->table->getColCount();$i++){
                    $this->table->setCellContents($row,$i,$contents);
                }
            }
            $attribute_array=array();
            if($class_td){$attribute_array['class']=$class_td;}
            if($rowspan){$attribute_array['rowspan']=$rowspan;}
            if($colspan){$attribute_array['colspan']=$colspan;}
            if($attribute_array){$this->table->updateRowAttributes($row,$attribute_array);}
        }
       
        //穴埋め
        public function setAutoFill($fill){
            $this->table->setAutoFill($fill);
        }
       
        //行毎に属性セット
        public function altRowAttributes($attribute1,$attribute2){
            $this->table->altRowAttributes(0,$attribute1,$attribute2);
        }
       
        //表示
        public function toHtml(){
            return $this->table->toHtml();
        }
       
    #↓クラスのおわり
    }


例によってエラー処理(ry
今度は逆に機能を纏め過ぎてて却って分かり難くなっているような気がしないでもないですが、そこらへんはスルーということで。
また、colspanとrowspanが重なった場合の処理に微妙なところがあるので、変則的なテーブルにはあまり使わない方がいいかもしれません。
まあとりあえずでは使ってみましょう。

table.php 

<?php
    #初期設定
    header("Content-type: text/html; charset=utf-8");
    require_once('./table.class.php');
    $table = new pearHtmlTableModel(1);
   
    $row=array('0','1','2','3','4','5','6','7','8','9');
    $table->setTableHeaders('row',$row);
   
    $table->setCellContents(8,8,'cell','idcell',2,2);
    $table->setColContents(3,'col','idcol',2,1);
    $table->setRowContents(3,'row','idrow',1,2);
   
    $table->altRowAttributes(array('bgcolor'=>'#ffa0a0'),array('bgcolor'=>'#a0ffa0'));
    $table->setAutoFill('__');
   
    $ret=$table->toHtml();
   
    print($ret);

出力結果は以下のようになります。

0 __ __ col __ __ __ __ __ __
1 __ __ __ __ __ __ __ __
2 __ __ col __ __ __ __ __ __
row row row row row
4 __ __ col __ __ __ __ __ __
5 __ __ __ __ __ __ __ __
6 __ __ col __ __ __ __ __ __
7 __ __ __ __ __ __ __ __
8 __ __ col __ __ __ __ cell
9 __ __ __ __ __ __

このようにさくさくとテーブルを作成することができます。
細かな調整は各自でやっていただくとして、データベースから取り出したデータをテーブルで表示したい場合などに威力を発揮するのではないかと思われます。



2008/09/12 15:57 | Comments(0) | TrackBack() | PHP
PHP1-24:Text_Highlighter

Text_Highlighter 0.7.1 (beta)

当サイトではPHPソースをテーブル内に入れて表示していますが、ぶっちゃけ見難いです。
他所のブログではソースに命令毎に色を付けたりハイライトしたりしているところがありますが、そういうことを自動で行ってくれるライブラリが存在します。
Text_Highlighterは、PHPだけでなくC++やPerlやRuby等の他言語、そしてHTMLやCSS、Javascript等にも対応しています。

例によってダウンロード後pear/Textフォルダに移動。
このText_Highlighter、pear/Text直下にHighlighter.phpとその他のxmlファイル等を置いて、Highlighterフォルダ内に各phpファイルを設置するという前提で書かれているようです。
個人的に美しくないので却下したいところですが、Highlighterフォルダ内の全ファイルを書き直さなければいけないようで面倒なのでやめました。

いつものようにとりあえずラッパクラス。

highlighter.class.php
 

<?php
    //初期設定
    define('PEAR_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/');
    ini_set('include_path',PEAR_DIR.PATH_SEPARATOR.ini_get('include_path'));
    require_once(PEAR_DIR.'Text/Highlighter.php');
   
    class pearHighLightModel {
        //メンバ変数
        private $highlight = '';
        private $language = 'PHP';
        private $renderer = 'Html';
        private $numbers = FALSE ;
       
        //コンストラクタ
        public function __construct(){
            $this->_makeHighlighter();
        }
       
        //ハイライトして返す
        public function highlight($str){
            return $this->highlight->highlight($str);
        }
       
        //言語を変更
        public function setLang($language){
            $this->language=$language;
            $this->_makeHighlighter();
        }
       
        //レンダリング変更
        public function setRenderer($renderer){
            $this->renderer=$renderer;
            $this->_makeHighlighter();
        }
       
        //表示形式を変更
        public function setNumbers($numbers){
            $this->numbers=$numbers;
            $this->_makeHighlighter();
        }
       
        //オブジェクト作る
        private function _makeHighlighter(){
            require_once('Text/Highlighter/Renderer/'.$this->renderer.'.php');
            $renderclass='Text_Highlighter_Renderer_'.$this->renderer;
            $this->highlight = Text_Highlighter::factory($this->language);
            $this->highlight->setRenderer(new $renderclass(array('numbers'=>$this->numbers)));
        }
       
    #↓クラスのおわり
    }


factoryに引き渡すのが表示させたい内容の形式、setRendererで渡すのが表示する形式です。
混乱しがちですので注意しましょう。
レンダリング方法はHTML形式が基本ですが、XMLやJSON等の形式にで出力することもできます。
ただ、その場合は何故か対応するレンダリングの設定ファイルをText_Highlighter側でincludeしてくれないので、手動でincludeしてやる必要があります。
デフォルトのHtml形式だけは自力でincludeしてるのに。

setNumbersの引数には、Highlighter.phpで定義されている定数を入れます。
現在、HL_NUMBERS_LI、HL_NUMBERS_TABLE、HL_INFINITY、FALSEが指定でき、それぞれ行番号を表示したりしなかったりというHTMLの出力形式に対応します。

レンダリング方法はsetRendererで変更できるのですが、最初はPHPのソースを表示させて次にJavaScriptのソースを表示したい、等という場合に変更する方法が何気にありません。
使用する側で別々にnewすればいいだけではありますが、とりあえずsetLangを作って毎回クラスを新規作成することにしました。

例によってエラー処理等を全然行っていないので、存在しない引数を渡したりするとエラーになります。

さて、実際に表示してみます。

highlighter.php 

<?php
    #初期設定
    header("Content-type: text/html; charset=utf-8");
    require_once('./highlight.class.php');
    $highlight = new pearHighLightModel();
   
    #適当に取得
    $str='array_pop($array);SELECT * FROM some_table WHERE id = 12';
    $ret[]=$highlight->highlight($str);
   
    $highlight->setLang('SQL');
    $highlight->setRenderer('XML');
    $ret[]=$highlight->highlight($str);
   
    $highlight->setLang('Html');
    $highlight->setRenderer('Html');
    $highlight->setNumbers(HL_NUMBERS_TABLE);
    $ret[]=$highlight->highlight($str);
   
    #表示
    foreach($ret as $val){
        print(htmlspecialchars($val).'<hr>');
    }



<div class="hl-main"><pre><span class="hl-code">array_pop($array);SELECT * FROM some_table WHERE id = 12</span></pre></div>
--------------------------------------------------------------------------------
<array> <XML_Serializer_Tag> <identifier>array_pop</identifier> </XML_Serializer_Tag> <XML_Serializer_Tag> <brackets>(</brackets> </XML_Serializer_Tag> <XML_Serializer_Tag> <code>$</code> </XML_Serializer_Tag> <XML_Serializer_Tag> <reserved>array</reserved> </XML_Serializer_Tag> <XML_Serializer_Tag> <brackets>)</brackets> </XML_Serializer_Tag> <XML_Serializer_Tag> <code>;</code> </XML_Serializer_Tag> <XML_Serializer_Tag> <reserved>SELECT</reserved> </XML_Serializer_Tag> <XML_Serializer_Tag> <code> * </code> </XML_Serializer_Tag> <XML_Serializer_Tag> <reserved>FROM</reserved> </XML_Serializer_Tag> <XML_Serializer_Tag> <code> </code> </XML_Serializer_Tag> <XML_Serializer_Tag> <identifier>some_table</identifier> </XML_Serializer_Tag> <XML_Serializer_Tag> <code> </code> </XML_Serializer_Tag> <XML_Serializer_Tag> <reserved>WHERE</reserved> </XML_Serializer_Tag> <XML_Serializer_Tag> <code> </code> </XML_Serializer_Tag> <XML_Serializer_Tag> <identifier>id</identifier> </XML_Serializer_Tag> <XML_Serializer_Tag> <code> = </code> </XML_Serializer_Tag> <XML_Serializer_Tag> <number>12</number> </XML_Serializer_Tag> </array>
--------------------------------------------------------------------------------
<div class="hl-main"><table class="hl-table" width="100%"><tr><td class="hl-gutter" align="right" valign="top"><pre>1 </pre></td><td class="hl-main" valign="top"><pre><span class="hl-code">array_pop($array);SELECT * FROM some_table WHERE id = 12</span></pre></td></tr></table></div>
 


このようにタグがついた上で出力されます。
あとは適当にcssで飾り付けましょう。


何気にPHPにはhighlight_stringという命令があるのですが、気にしたら負けです。
http://php.benscom.com/manual/ja/function.highlight-string.php
 

 



2008/09/10 12:53 | Comments(0) | TrackBack() | PHP

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