他言語では入出力ストリームを作成して云々、ファイルポインタを作成して云々、などと一手間かけないといけないのですが、PHPだと一瞬で終わってしまうのが楽なところでもありいい加減なところでもあります。
そんなわけで普通にWebアプリを作成する上では全く使用することはないのですが、意味もなく直接関数を書いて接続することも出来ます。
とりあえずソケット接続してみましょう。
Socket以外にもCurlやSOAPやその他諸々のネットワーク系関数がたくさんあるのですが正直さっぱりわかりません。
いったい何がどう違うんだ。
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
|
//送信内容
$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]: 無効な引数が提供されました。
無効とか言われましても。
以下をコピペしてユーザ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
|
//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のサーバ証明書とかインストールした覚えがないのだが、これはきちんと動いているのだろうか。
よくわからない。
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
|
//初期設定
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">
print($fck_form);
<input type="submit" name="a" value="送信">
</form>
</body>
</html>
|
post.php
1
2
|
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を使用してはいけません。
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
|
//初期設定
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
|
//初期設定
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アカウントからメールが送信されたはずです。
めでたし。
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 $qr->set_qrcode_data_string('sample'); //$image=$qr->getQRCode(); /* |
printQRCodeで画像として出力、getQRCodeでGDオブジェクトとして取得、saveQRCodeでファイルに保存出来ます。
と、せっかくここまで作成したところでQRコードクラスライブラリの存在に気付いて俺涙目。
QRコードは(株)デンソーウェーブの登録商標です。
本品のついてのあらゆる権利はY.Swetake氏が所有しますが、本品についての質問、要望等をY.Swetake氏に対して行わないでください。
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 /* |
printBarcodeを呼べばそのまま画像ファイルとして出力します。
saveBarcodeでファイルとして保存されます。
getBarcodeでGDオブジェクトとして取得できるので、取得したバーコードを更に変更を加える場合はこちらを使用します。
このクラス最大の問題点は、使用者がいないということでしょう。
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 |
出力結果は以下のようになります。
0 | __ | __ | col | __ | __ | __ | __ | __ | __ |
---|---|---|---|---|---|---|---|---|---|
1 | __ | __ | __ | __ | __ | __ | __ | __ | |
2 | __ | __ | col | __ | __ | __ | __ | __ | __ |
row | row | row | row | row | |||||
4 | __ | __ | col | __ | __ | __ | __ | __ | __ |
5 | __ | __ | __ | __ | __ | __ | __ | __ | |
6 | __ | __ | col | __ | __ | __ | __ | __ | __ |
7 | __ | __ | __ | __ | __ | __ | __ | __ | |
8 | __ | __ | col | __ | __ | __ | __ | cell | |
9 | __ | __ | __ | __ | __ | __ |
このようにさくさくとテーブルを作成することができます。
細かな調整は各自でやっていただくとして、データベースから取り出したデータをテーブルで表示したい場合などに威力を発揮するのではないかと思われます。
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
Text_Figlet 1.0.0
http://pear.php.net/manual/ja/package.text.text-figlet.php
_ _ _ _ _ ___ _ _ ./ / /_//_// / / /__/_/_/ / / _ _/ / / / / / / // _ /_/ /_/ / / フ /⌒ヽ . /  ̄ ̄ ̄/ ./ / / /  ̄ ̄/ / / __ ヽ  ̄/ // / / / / ̄/ / ̄ / / ./ / / /  ̄ | |/ /) ) / / // /  ̄ / / / / .| | | | | | ̄ \/ /_ / / /_ / / / / \ \ \ \ | | / / ( /  ̄  ̄  ̄  ̄  ̄  ̄  ̄
を作成できるという噂のText_Figlet。
誰だよこんなの登録した奴。あと許可した奴。
まずpearフォルダ内にText_Figletフォルダを作ってその中にTextフォルダ、fontsフォルダを移動します。
Text_Figletには実質的にメソッドが二つしかないのでわざわざラッパクラスを作らなくてもいいといえばいいのですが、手動でインストールしている関係上include_pathが通ってなかったりで面倒なのでとりあえず作ります。
figlet.class.php
<?php //初期設定 define('PEAR_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/'); define('PEAR_FIGLET_FONT_DIR',PEAR_DIR.'Text_Figlet/fonts/'); ini_set('include_path',PEAR_DIR.PATH_SEPARATOR.ini_get('include_path')); require_once(PEAR_DIR.'Text_Figlet/Text/Figlet.php'); class pearFigletModel { //メンバ変数 private $figlet = array(); //コンストラクタ public function __construct(){ $this->figlet = new Text_Figlet(); $a=$this->figlet->loadFont(PEAR_FIGLET_FONT_DIR.'slant.flf'); } //フォントをセット public function loadFont($fontname){ $this->figlet->loadFont(PEAR_FIGLET_FONT_DIR.$fontname); } //表示 public function lineEcho($str){ return $this->figlet->lineEcho($str); } #↓クラスのおわり } |
Text_Figletは、fontsフォルダではなく、include_pathおよびPearのコンフィグファイルで設定した場所からしかフォントを探してくれません。
include_pathを通してもいいのですが、今回は手っ取り早く定数にしています。
例によってPear::isErrorなんかは自力でどうにかしてください。
さっそく実行。
figlet.php
<?php #初期設定 header("Content-type: text/html; charset=utf-8"); require_once('./figlet.class.php'); $figlet = new pearFigletModel(); $str1='Hello, world'; $str2='STOP!地球温暖化。'; $ret1=$figlet->lineEcho($str1); $ret2=$figlet->lineEcho($str2); print("<pre>"); print($ret1.'<hr>'.$ret2); |
__ __ ____ __ __ / / / /__ / / /___ _ ______ _____/ /___/ / / /_/ / _ \/ / / __ \ | | /| / / __ \/ ___/ / __ / / __ / __/ / / /_/ / | |/ |/ / /_/ / / / / /_/ / /_/ /_/\___/_/_/\____( ) |__/|__/\____/_/ /_/\__,_/ |/ -------------------------------------------------------------------------------- /\// ___ /\// /\// __ /\// /\// _//\/_|_ |_//\/__//\/__/ _)_//\/__//\/_ / __ `/ __// __ `/ __ `/ | |/ __ `/ __ `/ / /_/ /____/ /_/ / /_/ / || / /_/ / /_/ / \__,_/ \__,_/\__,_/| |_/\__,_/\__,_/ (__/ |
う、読めない。
まあ当たり前で、同梱されているフォントには日本語が入っていません。
ではどうすればいいかとフォントファイルであるところのflfを覗いてみると、これが単なるテキストファイルです。
___ @ / |@ / /| |@ / ___ |@ /_/ |_|@ @@ ____ @ / __ )@ / __ |@ / /_/ / @ /_____/ @ @@ ______@ / ____/@ / / @ / /___ @ \____/ @ @@ |
よし、日本語は諦めよう。
Date_Holidays 0.20.1 (alpha)
Date_Holidays_Japan 0.1.0 (alpha)
PHPに限らずプログラマーにとって休日判定は頭の痛い問題のひとつです。
元日や春分秋分などの固定された日はまだいいとして、面倒なのが年によって日付が変更になる休日です。
体育の日を10月10日以外に変更するとかいう愚行のせいで、ある年までは祝日を固定にして、それ以降は毎年祝日を算出する必要があります。
また振替休日のチェックも面倒です。
例によってPearに祝日を扱うクラスがあるので使用してみましょう。
Pear::Date_Holidaysで祝日を扱うことが出来ます。
α版だけあって融通の利かない部分もあるのですが、祝日判定を間違うような致命的なバグは無いようですので使ってみましょう。
こちらはプラグイン的に言語を追加できるのですが、つい先日の2008/08/23に日本語言語ファイルであるところのDate_Holidays_Japanが追加されました。
いつものように解凍した後Date_Holidays_JapanフォルダをDate_Holidaysフォルダに上書きコピーし、フォルダ名をDateと変更します。
holiday.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.'Date/Holidays.php'); class pearHolidayModel { //メンバ変数 private $japan = array(); //コンストラクタ public function __construct($year=null){ $this->japan = &Date_Holidays::factory('Japan',$year,'ja_JP'); $this->japan->addTranslationFile(PEAR_DIR . 'Date/lang/Japan/ja_JP.xml', 'ja_JP'); if (Date_Holidays::isError($this->japan)){die('ファクトリでドライバオブジェクトを作成できませんでした');} } //一年の全祝日取得 public function getHolidays($year){ if(is_null($year)){$year=date('Y');} $this->japan->setYear($year); return $this->japan->getHolidays(); } //祝日ならその情報を取得//祝日でなかったらNULL public function getHolidayForDate($date=null){ if(is_null($date)){$date=date('Y-m-d');} return $this->japan->getHolidayForDate($date); } #↓クラスのおわり } |
Date_Holidaysはfactoryでインスタンスを作成します。
話は変わりますがどうもこのFactoryMethodパターンが理解できないのでどなたか教えてください。
利用側では常にインターフェイスのfactoryを呼んでおいてfactoryで実装クラスをnewすれば、実装クラスのメソッド名を変更してもfactoryを変更するだけで利用側を変える必要が無くて楽ちん、という理解で大丈夫なの?
以下簡単にメソッドを紹介すると、コンストラクタで国名と年を引き渡して祝日を決定します。
setYearで年を変更し、getHolidaysでその年の全祝日を取得します。
isHolidayは引数の日付が祝日かどうかを判定します。
getHolidayで祝日名から情報を取得、getHolidayForDateで日付から情報を取得します。
getHolidaysForDatespanで特定の期間中の祝日を取得、ということも出来るようです。
祝日を追加する関数については、_addStaticHolidays、_addHoliday、_addTranslationForHoliday、_addStringPropertyForHoliday、_addStringPropertiesForHoliday等ありますが違いがよくわからないし_なので触らぬほうがよさげ。
で、肝心のセットする関数がどれかはわかりませんでした。
デフォルトでは記念日名が英語ですが、addTranslationFileでDate_Holidays_Japanに入っていた言語ファイルを追加しています。
これを適用することで祝日名を日本語で取得することが可能になります。
さて呼び出してみましょう。
holiday.php
<?php #初期設定 header("Content-type: text/html; charset=utf-8"); require_once('./holiday.class.php'); $holiday = new pearHolidayModel(); $day='2007-01-01'; $today=$holiday->getHolidayForDate($day); var_dump($today); |
NULL
あれ?
日付から休日情報を取得するgetHolidayForDateは、引数にはY-m-dを要求するのにそれを見ていません。
setYearでセットした年とgetHolidayForDateに引き渡した年が違う場合、常にNULLが返ってきます。
isHolidayのほうはしっかり年もチェックしているのに。
見比べてみたところ、isHolidayには入っている以下のようなルーチンがgetHolidayForDateには入っていませんでした。
$compare_year = $date->getYear(); $this_year = $this->getYear(); if ($this_year !== $compare_year) { $this->setYear($compare_year); } |
このままでは不便きわまりないのでholiday.class.phpを変更。
//休日ならその情報を取得//祝日でなかったらNULL public function getHolidayForDate($date=null){ if(is_null($date)){$date=date('Y-m-d');} $year=explode('-',$date); $this->japan->setYear($year[0]); return $this->japan->getHolidayForDate($date); } |
これで年間の全祝日の取得、および日付から祝日情報を取得できるようになりました。
他にも必要なものがあればその都度追加しましょう。
つうかこのクラスは中身がよくわかんないんだよなあ…
何に使うのかよくわからないメソッドや定数がいっぱいあるし。