忍者ブログ
[PR]
×

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



2025/01/18 04:26 |
Pear::Net_Pingにバグ

Net_Pingの現時点での最新バージョン、2.4.3にはバグがあります。
http://pear.php.net/manual/ja/package.networking.net-ping.php

Net_Pingは指定したホストに対してpingを撃ってくれるPearパッケージですが、
そのメソッドのひとつ、checkHostはホストの生死判定を行います。
が、このメソッドを呼び出した場合、必ず下記のエラーが発生します。

Fatal error: Cannot use object of type Net_Ping_Result as array in Ping.php on line 425

該当部分は以下

preg_match_all('|\d+|', $res[3], $matches)

この$retはPear::pingの返り値なのですが、これはオブジェクトとして返ってきます。
オブジェクトに対して$ret[3]なんて指定は当然出来ませんのでエラーになります。

ロードマップには次回の更新予定が掲載されています。
http://pear.php.net/bugs/roadmap.php?package=Net_Ping&roadmapdetail=2.4.4#a2.4.4
>Windows XP
>Function CheckHost fails with error every time.

え?Windowsだけ?
それよりも2.4.3から一年も放置のままというのはどうなんだ。

それにしてもこのパッケージ、バージョン情報が
// $Id: Ping.php,v 1.47 2007/12/28 04:30:42 cconstantine Exp $
とかしか書いてないのだが本当に2.4.3?


エラーになる超簡単なサンプルコード。

ping.php 

1
2
3
4
<?php
    require_once "Net/Ping.php";
    $ping = Net_Ping::factory();
    $ping->checkHost('www.google.co.jp');
 

 

PR


2008/12/17 12:18 | Comments(0) | TrackBack() | PHP
PHP1-40:PHPでXML

XMLが何なのかについては未だにさっぱりわかりませんが、PHPにおいてのXMLの役割ははっきりしていて、まあ要するに単なるタグ付きのテキストです。

XMLを利用したXML-RPCというサービスがあり、これはXML文書をHTTPで送りつけることによって$_REQUESTより複雑な内容を送受信したりするサービスのことです。
そんなわけでXML-RPCのようなサービスにXML文書を送りつけたり、逆に送りつけられたXML文書を解析したりするのがPHPでのXMLの主な使い方になるでしょう。

特に意味はありませんがXML-RPCサービスでググると一番に出てくる郵便番号チェックを行うクライアントを作ってみましょう。
http://yubin.senmon.net/service/xmlrpc/

yuubin.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
<?php
 
    //XML
    $yuubin_xml='
        <methodCall>
          <methodName>yubin.fetchAddressByPostcode</methodName>
          <params>
            <param>
                <value><string>1000002</string></value>
            </param>
          </params>
        </methodCall>
    ';
 
    //URL
    $yuubin_href='http://yubin.senmon.net:80/service/xmlrpc/';
    
    //HTTPリクエスト作成
    $yuubin_context = stream_context_create(
        array(
            'http' => array(
            'method' => "POST",
            'header' => "Content-Type: text/xml",
            'content' => $yuubin_xml)
        )
    );
    
    //XML-RPC
    $ret=file_get_contents($yuubin_href, false, $yuubin_context);
 
    print("<pre>");var_dump($ret);die();

あっさり成功。
簡単すぎるだろ。

レスポンスは同様にXMLで返ってきますので、こちらを解析してみましょう。
XMLツールとしては、DOM、SAX、SimpleXML、XMLReader、XSLT等々がありますがどれ使えばいいんだよってかどう違うんだこれ。
http://jp2.php.net/manual/ja/refs.xml.php

最も簡単なのはsimplexml_load_stringで、XMLをそのまま突っ込むだけで配列になります。
http://jp2.php.net/manual/ja/function.simplexml-load-string.php

ここではXMLパース目的としては一番簡単とされているSimpleXMLを用いてパースしてみます。
リクエスト用XMLはxmlrpc_encode_requestを用いれば面倒なタグを書く必要はありません。
http://jp2.php.net/manual/ja/function.xmlrpc-encode-request.php

yuubin2.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
<?php
 
    //XML
    $yuubin_xml_method='yubin.fetchAddressByPostcode';
    $yuubin_xml_params=array('1000002');
    $yuubin_xml=xmlrpc_encode_request($yuubin_xml_method,$yuubin_xml_params);
    
    //URL
    $yuubin_href='http://yubin.senmon.net:80/service/xmlrpc/';
    
    //HTTPリクエスト作成
    $yuubin_context = stream_context_create(
        array(
            'http' => array(
            'method' => "POST",
            'header' => "Content-Type: text/xml",
            'content' => $yuubin_xml)
        )
    );
    
    //XML-RPC
    $yuubin_request=file_get_contents($yuubin_href, false, $yuubin_context);
    
    //SimpleXMLElement
    $yuubin_sxml=new SimpleXMLElement($yuubin_request);
    
    //XMLの一部を抽出
    foreach(
        $yuubin_sxml
        ->params
        ->param
        ->value
        ->array
        ->data
        ->value
        ->struct
        ->member
         as $yuubin_sxml_val){
            print($yuubin_sxml_val->name.':'
            .$yuubin_sxml_val->value->children()."<br>\n");
    }

あとは郵便番号を引数にして関数化したり、他のメソッドも実装してクラス化したりするとよいかもしれません。
あと先方でも指摘されているエラー処理も忘れずに。

それにしても->のところはもうちょっとどうにかならないのだろうか。


2008/12/12 20:17 | Comments(0) | TrackBack() | PHP
PHP1-39:ディレクトリでイテレータ

配列に対するイテレータ、オブジェクトに対するイテレータと来て次はディレクトリに対するイテレータを使ってみます。
http://docs.php.net/manual/ja/class.directoryiterator.php

    $dir=new DirectoryIterator(dirname(__FILE__));
    $dir->rewind();
    $filename=$dir->current();
    var_dump($filename);


object(DirectoryIterator)#2 (0) { }
NO!何だこれ!

DirectoryIterator::current   これ自身を返す (Iterator インターフェースに必要)
http://docs.php.net/manual/ja/directoryiterator.current.php

これまでの動作から言うとcurrentメソッドは現在のファイル名を返すべきであるのに、何故かインスタンス自身が返されます。

あと何気にnextで'.'や'..'、ディレクトリとファイルが混ざってやってきたりして面倒です。
ファイルのみを選び出すラッパクラスを作成しましょう。

DirectoryIteratorX.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
<?php
    
    class DirectoryIteratorX extends DirectoryIterator{
        
        //次のファイルを取得
        function nextFile(){
            while($this->valid() && !$this->isFile()){
                $this->next();
            }
        }
        
        //次のディレクトリを取得
        function nextDir(){
            while($this->valid() && !$this->isDir()){
                $this->next();
            }
        }
        
        //現在のファイルを取得
        function current(){
            return $this->getFilename();
        }
    
    }
    
    $dir=new DirectoryIteratorX(dirname(__FILE__));
    
    $dir->nextFile();
    $dir->current();

DirectoryIteratorクラスやSplFileInfoクラスはファイルに関する情報を扱うので、currentやnextといった基本以外のメソッドが大量に揃っています。
といっても作成、更新日時や読み書き属性等を拾ってくるようなメソッドばかりなので、触ってみればすぐにわかります。

手元のWindows環境では$dir->getPerms()で33206が返ってくるのですが、これは8進数にすると100666となり、パーミッション0666ということらしいです。
10は何?

 



2008/12/11 23:09 | Comments(0) | TrackBack() | PHP
PHP1-38:オブジェクトでイテレータ

new SplDoublyLinkedList();
Fatal error: Class 'SplDoublyLinkedList' not found

お?

spl_classes()で自分の環境に入っているSPL関連クラスを調べることが出来ます。

試してみたところ、何故かデータ構造関連のクラスが一切入っていませんでした。
何処にも書かれていませんが、どうやら現時点では開発中のPHP5.3以降でないと使用できないようです。

それ以外は入っていたのですが、SplDoublyLinkedListのかわりなのかなんなのかSplObjectStorageとかいうのが入っている。
見慣れないというかマニュアルに載っていません。

どうやらPHP6で実装されるクラスのようですが、何故か5.2.5の時点で既に存在しています。
とりあえず触ってみます。

splobjectstorage.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
<?php
    
    //適当なクラスを作成
        class testClass1{}
        class testClass2{}
        
        $a=new testClass1(0);
        $b=new testClass1(5);
        $c=new testClass2('x');
    
    //SplObjectStorageに代入
        $st=new SplObjectStorage();
        
        $st->attach($a);
        $st->attach($b);
        $st->attach($c);
    
    //SplObjectStorageの挙動
        $st->count();//オブジェクト数=3
        
        $st->contains($b);    //$bが含まれているのでtrue
        $st->contains(new testClass1(5));    //$bと違うインスタンスなのでfalse
        
        $st->current();        //null
        
        $st->rewind();        //$stのポインタ初期化
        $st->key();            //$aの順番=0
        $st->current();        //$a
    
        $st->next();
        $st->key();            //$bの順番=1
        $st->current();        //$b
        
        $st->next();
        $st->valid();        //3番目に$cが存在するのでtrue
        $st->next();
        $st->valid();        //4番目は存在しないのでfalse
        
        $st->detach($b);    //$bを削除
        $st->count();        //オブジェクト数=2

前回行った配列に関するイテレータと同じように、SplObjectStorageはオブジェクトに対してイテレータを適用するためのクラスのようです。
newの部分以外は、配列に関するイテレータとまったく同じように扱うことが出来ます。
素晴らしいですね。

最大の問題点は、使い道がよくわからないというところでしょうか。


ソースはこちらに載っているようですが、
http://www.php.net/~helly/php/ext/spl/splobjectstorage_8inc-source.html
これってPHPで実装されてるのか?

privateなインスタンス変数にアクセスした際、普通はCannot access private propertyエラーになるのだが、
$st->indexはUndefined propertyになるので、少なくともそのままの実装ではないような気がする。
 

 



2008/12/10 12:53 | Comments(0) | TrackBack() | PHP
PHP1-37:PHPでイテレータ

配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である
といわれても何のことだかさっぱりです。

PHPでは、配列にアクセスする際はforeachなりnextなりを使用し、
ディレクトリにアクセスする際はopendirやreaddirを使用し、
XMLを解析する場合はxml_set_element_handlerなりxml_set_character_data_handlerを使用したりするわけですが、これでは処理する相手が増えるたびに新しい関数を覚えなくてはいけなかったりして恐ろしく面倒です。

というわけでIteratorパターンなるものが編み出されました。
データ構造からデータを取得する手段を切り離すということです。

これを使用することによって、$iterator_array->next()で配列の次の値を取得し、$iterator_dir->next()で次のファイル名を取得し、$iterator_xml->next()でXMLの次の要素を取得できるようになるわけです。
とりあえず配列について簡単にIteratorを作成してみます。
例によってエラー処理は放置。

iterator_array.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
<?php
    class Iterator_array{
        
        public $array=array(
            '',
            '<',
            '&lt;',
            '!"#%&',
            '\''
        );
        
        function __construct(){
            $this->position=0;
        }
        
        public function current(){
            return $this->array[$this->position];
        }
        public function key(){
            return $this->position;
        }
        
        public function next(){
            $this->position++;
        }
        
        public function rewind(){
            $this->position=0;
        }
        
        public function seek($position){
            $this->position=$position;
        }
        
        public function valid(){
            return isset($this->array[$this->position]) ? true : false;
        }
        
        public function count(){
            return count($this->array);
        }
    
    #↓クラスのおわり
    }

index.php
1
2
3
4
5
6
7
8
9
10
<?php
    
    require_once('iterator.php');
    $iter=new Iterator_array();
    
    print($iter->current());
    $iter->next();
    print($iter->current());
    $iter->seek(4);
    print($iter->current());

さて、PHP5においてクラス関連機能が強化されたことに伴い、このように統一した操作を行うIteratorインターフェイスが用意されました。
せっかく自力でIterator_arrayクラスを作成したのに、内容のほぼ同じArrayIterator が実装されて俺涙目。


2008/12/08 19:19 | Comments(0) | TrackBack() | PHP
PHP1-36:再帰的処理

再帰的処理とは、一言でいうと自分自身を呼び出す関数です。

何が便利って連想配列全部に処理を行いたい場合などですかね。
連想配列に入ってきた文字列全てにhtmlspecialcharsを行う関数htmlspecialchars_arrayを作ってみましょう。

index.php 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
 
    //連想配列
    $input=array(
        '1'=>'abc',
        '2'=>'<>',
        '3'=>'#$%'
    );
    
    
    $output=htmlspecialchars_array($input);
    var_dump($output);
    
    
    //配列にhtmlspecialcharsする関数
    function htmlspecialchars_array($array){
        foreach($array as $key=>$val){
            $ret[$key]=htmlspecialchars($val);
        }
        return $ret;
    }


簡単にできました。
が、この関数、たとえば
 $input=array(array('1'=>'abc'));
なんてものが入ってくるとあっさりエラーになってしまいます。

こういう場合に威力を発揮するのが再帰的処理です。
たとえばXMLは入れ子が凄いことになっているわけですが、再帰的処理を使わずにあれを解析しようとするととんでもない労力が必要になります。
とりあえずhtmlspecialchars_arrayをパワーアップさせてみましょう。

recursive.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
<?php
    //連想配列
    $input=array(
        '1'=>'abc',
        '2'=>'<>',
        '3'=>array(
            '01'=>'aaa',
            '02'=>'><><'
        )
    );
    
    $output=htmlspecialchars_array($input);
    var_dump($output);
    
    //配列にhtmlspecialcharsする関数
    function htmlspecialchars_array($input){
        
        if(is_array($input)){
            foreach($input as $key=>$val){
                $ret[$key]=htmlspecialchars_array($val);
            }
            return $ret;
        }
        $ret=htmlspecialchars($input);
        return $ret;
    }


関数htmlspecialchars_arrayでは、まず引数が配列かどうかチェックします。
配列であれば配列の中の各値についてさらにhtmlspecialchars_arrayを呼び出します。
引数が配列でなければ、htmlspecialcharsして返します。
これを繰り返した結果、$input内の全ての値に対してhtmlspecialcharsを適用することができます。

このように深さが何段階あるかわからないが、そういうものに対してチェックを行いたい場合に役立ちます。
ディレクトリを順にチェックしたり、XMLを解析したり。
あるいは迷路を解くようなプログラムも作れてしまうかもしれません。
まあPHPでそこまでやる意味はわかりませんが。

array_walk_recursive?
細かいことは気にするな。


ちなみにarray_walk_recursiveを使った場合はこうなります。

array_recursive.php 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
    //連想配列
    $input=array(
        '1'=>'abc',
        '2'=>'<>',
        '3'=>array(
            '01'=>'aaa',
            '02'=>'><><'
        )
    );
    
    $output=htmlspecialchars_array($input);
    var_dump($output);
    
    function htmlspecialchars_array($input){
        array_walk_recursive(
            $input,
            create_function('&$val, $key', '$val=htmlspecialchars($val);')
        );
        return $input;
    }

create_functionわかりにくいよcreate_function。


2008/11/24 19:10 | Comments(0) | TrackBack() | PHP
PHP1-35:スタティック変数

クラスというのはあくまで設計図であり、インスタンス化することによって初めて実体化します。
一旦実体化されたクラスのインスタンスは、当然元のクラスとは別のものです。
一つのクラスから複数のインスタンスを作成しても、それぞれお互いには無関係になります。
一枚の設計図から複数の家を建てても、それぞれの家が無関係なのと同じことです。

例によって例外があります。
今回は例外のひとつ、スタティック変数について扱ってみます。

通常のインスタンス変数がインスタンスに属するのに対し、スタティック変数はクラスに属しています。
設計図の例でいうならば、インスタンス変数は実際に建てられた各家の間取りや床面積等に値します。
ひとつの家の間取りを変更しても、他の家の間取りにはまったく影響しません。

それに対し、スタティック変数は家の設計図そのものをいじくることに値します。
普通の設計図と違うのは、家を建てた後に設計図を変更してもその後に建てる家しか変更されませんが、スタティック変数の変更は全ての家に及ぶことです。

まあ例で説明したところでわかりづらいだけなので、実際に使ってみましょう。

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
<?php
 
class test{
    
    //static変数
    static $point=0;
    
    function getPoint(){
        return self::$point;
    }
    
#↓クラスのおわり
}
 
    $class1=new test();
    $val[]=$class1->getPoint();
    
    test::$point=10;
    $class2=new test();
    $val[]=$class2->getPoint();
    $val[]=$class1->getPoint();
 
    print("<pre>");var_dump($val);die();


変数宣言の時にstaticを付けることで、その変数はインスタンス変数ではなくスタティック変数になります。
スタティック変数はクラスに属しているため、自分自身のインスタンスを示す$thisではアクセスすることができません。
クラス名::$スタティック変数名という形で直接アクセスします。

最初に$class1->getPointした時点では、$pointは最初のままの0です。
その後self::$point=10と直接スタティック変数$pointを書き換えると、全てのクラスにこの変更が適用されます。
その後newした$class2はもちろん、$class1の$pointも10に変更されてしまいます。

まあ簡単に言うと、クラス内のグローバル変数です。

スタティック変数を使うと何が便利かってSingletonパターンが簡単に作成できます。

singleton.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
<?php
 
    //Singletonクラス
    class Singleton{
        
        static $single=0;
        
        private function __construct(){}
        
        function getInstance(){
            if(!self::$single){
                self::$single=new Singleton();
            }
            return self::$single;
        }
        
    #↓クラスのおわり
    }
    
    //作成
    $a=Singleton::getInstance();
    $b=Singleton::getInstance();
    
    //比較
    if($a===$b){
        print('true');
    }else{
        print('false');
    }


普通にnewするとコンストラクタで必ず新しいインスタンスが作成されてしまうので、コンストラクタをprivateにしてnewできないようにしてしまいます。
getInstanceメソッドは、もし既に自分自身が存在していれば自分自身を返します。
その結果何度getInstanceを参照しても、その中身は同じものとなります。
$aと$bという二軒の家を建てたつもりが、実は名前が違うだけで同じ家だったようなものです。
同じ家なので、$a->var=10;したあとに$b->varを参照すると10になっていたりします。

プログラム全体でインスタンスを一つしか作らせたくないようなクラスは、これをextendsして作りましょう。
まあリクエスト一回で終了するPHPにSingletonとかあんまり意味無いような気もしますが。


ログを読み返したら今までずっとインスタンス変数をクラス変数と呼んでいました。
過去のクラス変数はインスタンス変数に脳内変換してください。



2008/10/31 11:29 | Comments(0) | TrackBack() | PHP
PHP1-34:PHPでカプレカその2

前回のカプレカ操作は、各引数を数値として計算しました。
マイナスするだけなので非常に簡単ではあるのですが、PHPの整数演算は大きな桁数を扱うことが出来ません。
その大きさはOSに依存するのですが、一般的な32ビットOSでわずか10桁程度です。

引数を文字列や配列として与えた場合、ほぼ無制限と言えるほど大量のデータでも扱うことが出来ます。
カプレカ操作を文字列や配列を使用して行ってみます。

kaprekar2.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
<?php
    //配列でカプレカ
    
    //検証する数
    if(isset($_REQUEST['k'])){
        $seed=$_REQUEST['k'];
    }else{
        $seed='32799999999999999999999999968';
    }
    
    //準備
    $i=0;
    $result_array=array();
    $result_array[0]=$seed;
    
    while(1){
        //カプレカ
        $tmp=kaprekar($result_array[$i]);
        
        //返り値が既に存在すれば終了
        if($a=array_search($tmp,$result_array)){
            $result_array[$i+1]=$tmp.'='.$a;
            break;
        }
        
        $result_array[$i+1]=$tmp;
        $i++;
    }
    
    print("<pre>");print_r($result_array);die();
    
    //一回のカプレカ
    function kaprekar($seed){
        //繰下がり用の変数
        $borrow=0;
        
        //配列に分割
        $seed_array=str_split($seed);
        
        //小さい順に並べる
        rsort($seed_array);
        $seed_max=count($seed_array)-1;
        
        //差を計算
        for($i=$seed_max;$i>=0;$i--){
            //各桁の値
            $ret_array[$i]=$seed_array[$i]-$seed_array[$seed_max-$i]-$borrow;
            $borrow=0;
            //繰下がり
            if($ret_array[$i]<0){
                $ret_array[$i]+=10;
                $borrow=1;
            }
        }
        
        //文字列として返却
        ksort($ret_array);
        return implode('',$ret_array);
    }

前回の引数は数値でしたが、今回は文字列です。
それ以外はfunction kaprekar()以外は全く同じです。

今回のkaprekar()は少々分かり難いですが、やってることは昔懐かし繰下がりのある引き算です。
実際引き算を行うときに、一気に計算できる人なんてまず居ないでしょう。
下の位から順番に、一桁ずつ計算しているはずです。
今回はその計算方法を使用しています。

引数として32768が与えられたとして、まず以下のように一文字ずつに分解します。
Array
(
    [0] => 8
    [1] => 7
    [2] => 6
    [3] => 3
    [4] => 2
)

for内では下の位から順に引き算を実行します。
最初は$i=4、$borrow=0なので
$ret_array[4]=Array[4]-Array[0]-0=2-8=-6となります。

マイナスの場合は上の位から1を借りてこないといけないので$borrow=1をセットします。
そして借りてきた1を使って現在の位を+10します。
これで$i=4の処理が完了となります。

次に$i=3の場合、$ret_array[3]を同様に計算します。
今度は$i=3、$borrow=1なので
$ret_array[3]=Array[3]-Array[1]-1=3-7-1=-5となります。
$borrowは使ったので0に戻します。
もっとも、答えがまたマイナスなのですぐに1に戻りますが。

同様に、$i=2、$i=1、$i=0と最後の桁まで繰り返して終了。

今度のkaprekar()は、せいぜい2桁の計算しかしていないにもかかわらず、メモリが許す限り大きな桁の計算が出来てしまいます。

出力例
Array
(
    [0] => 32799999999999999999999999968
    [1] => 76320999999999999999999987633
    [2] => 97666332209999999998776633321
    [3] => 98777666662110988873333322211
    [4] => 98777655543332966665444322211
    [5] => 88655533332110988876666444312
    [6] => 97776643333210988766665332221
    [7] => 98765554333331976666654443211
    [8] => 88754333332110988876666654212
    [9] => 97776654333320987666654332221
    [10] => 98655543333320987666665444311
    [11] => 98764333332110988876666653211
    [12] => 98777654333331976666654322211
    [13] => 88755543333320987666665444212
    [14] => 97665433332110988876666543321
    [15] => 98776543333320987666665432211
    [16] => 98765543333320987666665443211
    [17] => 98765433333210988766666543211
    [18] => 98776543333320987666665432211=15
)

前回は計算できなかったこのくらい大きな桁の計算もできてしまいました。

このようにアルゴリズムを工夫することで、いろいろな場合において制限を回避することが出来るのですが、何故か工夫すればするほど人間にはわかりづらい表記になってしまうという不思議。
 

 



2008/10/17 10:31 | Comments(0) | TrackBack() | PHP
PHP1-33:PHPでカプレカその1

Wikipediaでいうところの定義2のほうのカプレカ操作を行ってみる。

各桁の数値を大きい順に並べた数から、各桁の数値を小さい順に並べた数を引く、という操作を繰り返すと最後は6174のような決まった数、もしくはループになるというものだ。
というわけでとりあえずさくっと作ってみましょう。

kaprekar.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
<?php
    //カプレカ操作をしてみる
    
    //検証する数
    if(isset($_REQUEST['k'])){
        $seed=(int)$_REQUEST['k'];
    }else{
        $seed=12345679;
    }
    
    //準備
    $i=0;
    $result_array=array();
    $result_array[0]=$seed;
    
    while(1){
        //カプレカ
        $tmp=kaprekar($result_array[$i]);
        
        //返り値が既に存在すれば終了
        if($a=array_search($tmp,$result_array)){
            $result_array[$i+1]=$tmp.'='.$a;
            break;
        }
        
        $result_array[$i+1]=$tmp;
        $i++;
    }
    
    print("<pre>");print_r($result_array);die();
    
    //一回のカプレカを計算する関数
    function kaprekar($int){
        is_int($int) or die('Not integer');
        
        //配列に入れる
        $int_array=str_split($int);
        
        //小さい順
        sort($int_array);
        $int_d=implode('',$int_array);
        
        //大きい順
        rsort($int_array);
        $int_u=implode('',$int_array);
        
        //
        return $int_u-$int_d;
    }


引数にk=123というふうに適当な数値を与えると引数のカプレカを操作します。
引数は必ず文字列としてやってきますが、is_intはPHPの関数としては珍しく型を厳格に評価するので、(int)で整数型に変換する必要があります。

出力例 

Array
(
    [0] => 12345679
    [1] => 85308642
    [2] => 86308632
    [3] => 86326632
    [4] => 64326654
    [5] => 43208766
    [6] => 85317642
    [7] => 75308643
    [8] => 84308652
    [9] => 86308632=2
)


まあなんの問題もなく作れてしまいました。
しかし$seedを大きくしてみると、ほんの10桁程度で計算不能になってしまいます。
これは各要素を数値として扱っているからです。

次回はもっと大きな数のカプレカ計算を行えるようにしてみます。



2008/10/14 16:55 | Comments(0) | TrackBack() | PHP
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ベースのカレンダーを使用した方がいいでしょう。

 



2008/10/09 11:18 | Comments(0) | TrackBack() | PHP

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