http://www.tokyo-np.co.jp/article/economics/news/CK2009042502000075.html
http://www.business-i.jp/news/sou-page/news/200904250109a.nwc
http://gigazine.net/index.php?/news/comments/20090424_china_sourcecode/
| 中国政府が近いうちにデジタル家電やIT製品の根幹となるソフトウェアの設計図「ソースコード」を各メーカーに強制開示させる方針であることが明らかになりました。 |
だいぶ前からこの話は出ていたのですが、ついに強行を決めたようです。
中国は現在もインターネット接続をかなり制限・遮断しています。
対象は反政府的なサイト、台湾系、チベット系のサイト、及びそれらのの情報が簡単に入手できるYouTube等々多岐にわたっています。
しかしいくら遮断しても、存在する限りどこからともなく情報を入手できてしまうのがインターネットというものです。
というわけでどうやら今度はITそのものを排除することを決定したようです。
政府の方針としては軸はぶれてませんね。
つきあう企業が幾つあるのか知りたいところです。
リフレクションを使うとクラスや関数の中身を解析することが出来ます。
PHPって基本的にソースが丸見えなんで、リバースエンジニアリングの意味がないような気がしないでもないんですが。
PHPの組み込み関数やエクステンションなどはPHPで書かれているわけではないので、そういう場合に中身を知りたい時に使うといいかもしれませんが、組み込み関数ならマニュアルを見ればいいし、いざとなればソースを見た方がより正確です。
まあソースはCなんでちょっと読めないのですが、そういう私みたいな中途半端な人がリフレクション使って役に立つものなんですかね?
使用法としては
・ReflectionFunctionに関数を突っ込む
・ReflectionClassにクラスを突っ込む
・ReflectionObjectにオブジェクトを突っ込む
・ReflectionMethodにメソッドを突っ込む
・ReflectionPropertyにクラス変数を突っ込む
・ReflectionExtensionにモジュールを突っ込む
となります。
その後は適当なメソッドを実行するだけで、例えばReflection::export()で簡単に内容の一覧を取得できます。
それ以外にも多様なメソッドで内容を解析できます。
Reflection_Extension.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
$ret=array();
//エクステンションの一覧
$ext=get_loaded_extensions();
//各エクステンションに対して
foreach($ext as $val){
$ref=new ReflectionExtension($val);
//エクステンション名
$ref_name =$ref->getName();
//中に入っているクラス
$ret[$ref_name]['class']=$ref->getClasses();
//中に入っている関数
$ret[$ref_name]['function']=$ref->getFunctions();
}
print("<html><pre>");var_dump($ret);die();
|
凄く簡単ですが、この例では、全てのエクステンションに対して、順番にクラスの一覧と関数の一覧を取得しています。
正直、なんのために使えばいいのかよくわからないんですが。
たとえばここのCurrency converterでは呼び出すメソッドを変更していますが、
$CurrencyConverter->{$_GET['currency1'].'-'.$_GET['currency2']}
でいいじゃない、とか。
開発時専用の機能のような気がしてならない。
Javaではメソッドの引数に必ず引数の型を指定する必要がありますが、PHPでもPHP5以降、関数とメソッドに対して引数の型を指定することが出来ます。
これによりメソッド呼び出しの時点で不正な呼び出しをチェックすることができ、メソッドの頭に毎回引数チェックを書く必要がなくなります。
ただ、最初に最大の問題点を挙げておくと、タイプヒンティングの指定がarray型とObject型しか使えないということです。
どうしてfunction func(string $str)ってできないんだよ……
とりあえず簡単な例。
hinting_test.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//適当なクラス
class hinting_test{
function hinting_array(array $arr){
return true;
}
}
//引数
$arg1=array('1','2');
$arg2='1,2';
$hint=new hinting_test();
$hint->hinting_array($arg1);
// $hint->hinting_array($arg2); //ここでCatchable fatal error発生
|
hinting_test::hinting_array()の引数にarrayを指定しているため、array以外の引数を渡すとエラーが発生します。
どうしてExceptionじゃないのかは謎ですが、これでJavaのように引数レベルで型を制御することができます。
ただしJavaで言うところのオーバーロードは使用できません。
function hinting_array(array $arr){}
function hinting_array(object $arr){}
と書くとredeclareエラーになります。
まったくもってどうでもいいんですが、長い間「タイプヒンディング」だと思っていました。
謎。
いいかげん終わろうと思いますが、最後にenum型を作成。
しようと思ったんですが、enum型の意味がいまいちわからない。
予め決められた値しか取れない型とかそんな解釈でいいんですかね。
なんかいくら解説を読んでも意味がわからんし人や言語によって違うこと言ってね?という感じもするし。
こんな理解していない人物がSJC-WCとか持っていていいんだろうか。
enumArray.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
|
class enumArray implements ArrayAccess,IteratorAggregate,Countable{
//適用可能な型
private $type_valid=array(
'string'
,'integer'
,'float'
,'boolean'
,'enum'
);
//適用可能な値 //現在enumのみ使用
private $arr_valid=array();
//型
private $type='';
//値入れ
private $arr=array();
//コンストラクタ
function __construct($type_valid='integer',$arr_valid=false){
//引数が適用可能な型でなければException
if(!in_array($type_valid,$this->type_valid)){
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not valid type in line <'.$debug[0]['line'].'>'
);
return false;
}
$this->type=$type_valid;
//enumの場合第二引数をチェック
if($type_valid=='enum'){
if(!is_array($arr_valid)){
throw new InvalidArgumentException(
'Type enum needs Argument Array'
);
}
$this->arr_valid=$arr_valid;
}
return true;
}
//マジックメソッド__set
public function __set($a,$b){
return $this->offsetSet($a,$b);
}
//マジックメソッド__get
public function __get($a){
return $this->offsetGet($a);
}
//引数の型チェック
private function _chechOffsetType($b){
switch($this->type){
case 'string':
return is_string($b);break;
case 'integer':
return is_int($b);break;
case 'float':
return is_float($b);break;
case 'boolean':
return is_bool($b);break;
case 'enum':
return $this->_is_enum($b);break;
}
return false;
}
//enumの型チェック
private function _is_enum($b){
return in_array($b,$this->arr_valid,true);
}
//値セット ArrayAccess::offsetSet
public function offsetSet($a,$b){
//引数が正しい型かチェック
if(!$this->_chechOffsetType($b)){
//正しい型でなければException
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not '.$this->type.' in line <'.$debug[0]['line'].'>'
.' type <'.gettype($b).'>'
);
return false;
}
//挿入
if($a===NULL){
$this->arr[]=$b;
}else{
$this->arr[$a]=$b;
}
return true;
}
//値が存在するか ArrayAccess::offsetExists
public function offsetExists($a){
if($a===NULL){return false;}
return isset($this->arr[$a]);
}
//値を取得 ArrayAccess::offsetGet
public function offsetGet($a){
if($this->offsetExists($a)){
return $this->arr[$a];
}else{
return null;
}
}
//値削除 ArrayAccess::offsetUnset
public function offsetUnset($a){
if($this->offsetExists($a)){
unset($this->arr[$a]);
}
}
//個数カウント Countable::count
public function count(){
return count($this->arr);
}
//イテレータ IteratorAggregate::getIterator
public function getIterator(){
return new ArrayIterator($this->arr);
}
#↓クラスのおわり
}
|
とりあえず第一引数が'enum'の場合のみ第二引数を配列で受け取り、その後代入する場合、配列にある値しか受け取らないという作成方針です。
なんかenumとは違うような気が激しくしないでもないのだが、まあいいか。
enumArray.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//投入可能な値
$enum_array=array(
1
,'a'
,true
);
//インスタンス
require_once('enumArray.class.php');
$str=new enumArray('enum',$enum_array);
//代入
try{
$str[]=1;
$str[]='1'; //ここでInvalidArgumentException
}catch(InvalidArgumentException $e){
print($e->getMessage());
}
print("<pre>");var_dump($str);
|
インスタンス作るときに一緒に投入可能な値を投げると、それ以外の値は代入することが出来なくなります。
…やっぱりenumとはなんか違うよなあ。
前回のをさらに改良。
intArrayをInteger以外にも対応するように変更しました。
classArray.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
|
class classArray implements ArrayAccess,IteratorAggregate,Countable{
//適用可能な型
private $type_valid=array(
'string'
,'integer'
,'float'
,'boolean'
);
//型
private $type='';
//配列
private $arr=array();
//コンストラクタ
function __construct($type_valid='integer'){
//引数が適用可能な型でなければException
if(!in_array($type_valid,$this->type_valid)){
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not valid type in line <'.$debug[0]['line'].'>'
);
return false;
}
$this->type=$type_valid;
return true;
}
//マジックメソッド__set
public function __set($a,$b){
return $this->offsetSet($a,$b);
}
//マジックメソッド__get
public function __get($a){
return $this->offsetGet($a);
}
//引数の型チェック
private function _chechOffsetType($b){
switch($this->type){
case 'string':
return is_string($b);break;
case 'integer':
return is_int($b);break;
case 'float':
return is_float($b);break;
case 'boolean':
return is_bool($b);break;
}
return false;
}
//値セット ArrayAccess::offsetSet
public function offsetSet($a,$b){
//引数が正しい型かチェック
if(!$this->_chechOffsetType($b)){
//正しい型でなければException
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not '.$this->type.' in line <'.$debug[0]['line'].'>'
.' type <'.gettype($b).'>'
);
return false;
}
//挿入
if($a===NULL){
$this->arr[]=$b;
}else{
$this->arr[$a]=$b;
}
return true;
}
//値が存在するか ArrayAccess::offsetExists
public function offsetExists($a){
if($a===NULL){return false;}
return isset($this->arr[$a]);
}
//値を取得 ArrayAccess::offsetGet
public function offsetGet($a){
if($this->offsetExists($a)){
return $this->arr[$a];
}else{
return null;
}
}
//値削除 ArrayAccess::offsetUnset
public function offsetUnset($a){
if($this->offsetExists($a)){
unset($this->arr[$a]);
}
}
//個数カウント Countable::count
public function count(){
return count($this->arr);
}
//イテレータ IteratorAggregate::getIterator
public function getIterator(){
return new ArrayIterator($this->arr);
}
#↓クラスのおわり
}
|
new時に'boolean','integer','double','string'何れかの引数を指定できます。
そしてその後、それ以外の型の値を代入するとエラーになります。
判定は厳密に行われるので、string型にintegerを放り込むこともできません。
前回から変更したのはコンストラクタとoffsetSetです。
コンストラクタでは引数として型を指定し、offsetSetではコンストラクタの引数として指定した型で型チェックを行います。
型チェック部分は最初evalとかで簡単にできないかなーと思ったんですが、いまいちうまく行かないのでやめました。
evalは怖いですし。
classArray.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//インスタンス
require_once('classArray.class.php');
$str=new classArray('string');
//代入
try{
$str[]='1';
$str[]=1; //ここでInvalidArgumentException
}catch(InvalidArgumentException $e){
print($e->getMessage());
}
print("<pre>");var_dump($str);
|
インスタンス生成時に'string'や'double'等と書くと、その型しか格納できない配列が作成できます。
値として真偽値のみが許される配列を作りたい、といった場合に使えるかも使えないかも。
前回のintArray.phpをちょっとだけ改造。
intArrayの各要素には$int['a']といった形でアクセスできますが、$int->aとしようとすると、勝手にintArray::aというメンバが作成されてしまいます。
では$int->aの挙動の変更は出来ないのか。できます。
intArray.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
|
class intArray implements ArrayAccess,IteratorAggregate,Countable{
//配列
private $arr=array();
//コンストラクタ
function __Construct(){}
//__set
public function __set($a,$b){
return $this->offsetSet($a,$b);
}
//__get
public function __get($a){
return $this->offsetGet($a);
}
//値セット ArrayAccess::offsetSet
public function offsetSet($a,$b){
//引数が数値かチェック
if(!is_int($b)){
//数値でなければエラー
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not integer in line <'.$debug[0]['line'].'>'
.' type <'.gettype($b).'>'
);
return false;
}
//挿入
if($a===NULL){
$this->arr[]=$b;
}else{
$this->arr[$a]=$b;
}
return true;
}
//値が存在するか ArrayAccess::offsetExists
public function offsetExists($a){
if($a===NULL){return false;}
return isset($this->arr[$a]);
}
//値を取得 ArrayAccess::offsetGet
public function offsetGet($a){
if($this->offsetExists($a)){
return $this->arr[$a];
}else{
return null;
}
}
//値削除 ArrayAccess::offsetUnset
public function offsetUnset($a){
if($this->offsetExists($a)){
unset($this->arr[$a]);
}
}
//個数カウント Countable::count
public function count(){
return count($this->arr);
}
//イテレータ IteratorAggregate::getIterator
public function getIterator(){
return new ArrayIterator($this->arr);
}
#↓クラスのおわり
}
|
__get()および__set()を実装すると、存在しないプロパティにアクセスした際にそのメソッドが呼び出されます。
http://jp.php.net/manual/ja/language.oop5.overloading.php#language.oop5.overloading.members
intArray.php
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//インスタンス
require_once('intArray.class.php');
$int=new intArray();
//代入
$int[2]=10;
$int->a=20;
//取得
print($int->{2});
print($int['a']);
|
$int->a=10;
と書くと、intArrayクラスにaというプロパティは無いのでintArray::__set('a',10)というメソッドが呼ばれます。
これを利用してオブジェクト的、配列的どっちにでもアクセスできるようになりました。
まあ値の代入はArrayAccess::offsetSet()、ArrayAccess::offsetGet()で実装されているので、__set()、__get()自体はそれらを呼んでるだけという簡単実装です。
以上で、$int['a']=10と値を代入し、$int->aで値を取り出すことが出来ます。
まあ混在させると分かり難いだけなのでどちらかに統一すべきなんですが。
privateなメンバに対しても__set、__getが優先するので$int->arr=10;と書いても大丈夫です。
publicなメンバがあった場合のみ、そちらに対しての上書きが優先されます。
今回はpublicなメンバは存在しないので失敗する可能性はありません。
ちなみに数値で始まるメンバ変数にアクセスする場合、$int->{'1a'}と{}で括ります。
数値だけのメンバならさらに$int->{1}でもアクセスできます。
全くどうでもいいんですが、普通の変数でも${1}と数字だけの変数を作成できます。
激しく気持ち悪い。
前回作成したintArrayは一見配列のように代入取得できますが、foreachで値を取得しようとすると正しく動作しません。
intArrayをvar_dumpしてみると中身はこのようになっています。
object(intArray)#1 (1) {
["arr:private"]=>
array(2) {
[0]=>
int(123)
["abc"]=>
int(456)
}
}
PHPはObjectをforeachにかけると、その中のpublicなメンバ変数に順にアクセスします。
この場合privateしかないので、foreachしても何も返ってこないことになります。
foreachできない配列なんて役に立ちませんので、イテレータを実装してみます。
Iteratorを実装することでforeachができるようになり、ArrayAccessを実装することで配列としてアクセスすることができます。
で、この両者を足してみると配列そのものとなるわけです。
という方針で作ろうと思ってたのですが、連想配列に対してArrayAccess::offsetSet()とIterator::next()あたりを実装するあたりでおそろしく面倒なことになったのでとりあえずキャンセル。
作れないことはないのですが見難きことこの上ないソースになってしまった。
あまりに美しくないのでもっと綺麗な書き方を思いつくまで封印。
というわけでSPLを活用して簡単に実装。
intArray.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
|
class intArray implements ArrayAccess,IteratorAggregate,Countable{
//配列
private $arr=array();
//コンストラクタ
function __Construct(){}
//値セット ArrayAccess::offsetSet
public function offsetSet($a,$b){
//引数が数値かチェック
if(!is_int($b)){
//数値でなければエラー
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not integer in line <'.$debug[0]['line'].'>'
.' type <'.gettype($b).'>'
);
return false;
}
//挿入
if($a===NULL){
$this->arr[]=$b;
}else{
$this->arr[$a]=$b;
}
return true;
}
//値が存在するか ArrayAccess::offsetExists
public function offsetExists($a){
if($a===NULL){return false;}
return isset($this->arr[$a]);
}
//値を取得 ArrayAccess::offsetGet
public function offsetGet($a){
if($this->offsetExists($a)){
return $this->arr[$a];
}else{
return null;
}
}
//値削除 ArrayAccess::offsetUnset
public function offsetUnset($a){
if($this->offsetExists($a)){
unset($this->arr[$a]);
}
}
//個数カウント Countable::count
public function count(){
return count($this->arr);
}
//イテレータ IteratorAggregate::getIterator
public function getIterator(){
return new ArrayIterator($this->arr);
}
#↓クラスのおわり
}
|
こういう場合のためにArrayIteratorなんてものが出来ていますのでこれを使ってさくっと実装しました。
http://jp.php.net/manual/ja/class.arrayiterator.php
配列を突っ込むとそれに対してイテレータでアクセスすることができるようになります。
IteratorAggregateは、自分でイテレータを実装するかわりに別のクラスに実装を丸投げしてしまうという他人任せのインターフェイスです。
http://jp.php.net/manual/ja/class.iteratoraggregate.php
で、本来ならIteratorAggregate::getIteratorで返すインスタンス内に各イテレータの実装を書かないといけないわけですが、そこに対してArrayIteratorを突っ込めばそこらへんが自動的にできてしまいます。
IteratorAggregateとArrayIteratorを使用することでロジックをほとんどいじらずforeachを実装できてしまいました。
ArrayAccess::offsetSet()なんかは値を返す必要はないのですが、気分的に入れています。
これは大丈夫なのだろうか?
以上でforeachもできるほとんど配列的なクラスintArrayが作成できました。
ただ、これでint型しか格納できない配列が作れて万々歳かと思いきや、やはり実体はObjectなわけで、in_array()やarray_merge()といった配列関数に突っ込むとエラーになってしまいます。
in_array('666',$int);
みたいな検索を行うことが出来ません。残念。
CheckIP 1.2.1 (stable)
http://pear.php.net/package/Net_CheckIP
CheckIP2 1.0.0RC2 (beta)
http://pear.php.net/package/Net_CheckIP2
Pear公式に登録されているPearパッケージは現時点で500ちょいですが、その中で最も役に立たないライブラリはどれかと言われたら、私はCheckIPを挙げます。
まあ、とりあえず使ってみましょう。
checkip.php
1
2
3
4
5
6
7
8
|
require_once('Net/CheckIP.php');
$ret[]=Net_CheckIP::check_ip('192.168.0.1');
$ret[]=Net_CheckIP::check_ip('192.168.0.256');
var_dump($ret);
|
何をやっているかというと、IPアドレスが正しい形式かどうかチェックしています。
正しい形式というのがどの程度の正しさかというと、別に到達可能性をチェックしているわけでもなければIPv6に対応しているわけでもなくサブネットすら考慮しない、単なる形としてのIPアドレスです。
中身を見てみると.でexolodeして各値が0~255の間かチェックしてました。
で、数値のチェックにpreg_matchを使っていましたが、だったら最初から一発ですりゃいいのに。
そもそも標準関数だけで簡単に出来てしまうことをPear化する意味がわからない。
さて、あまりにしょっぱい内容だったせいかCheckIP2という後継パッケージが出ました。
今回はIPv6対応なんだろうな、IPv6の正規表現は確かに省略とかあって面倒だからなあ
checkip2.php
1
2
3
4
5
6
7
8
|
require_once('Net/CheckIP2.php');
$ret[]=Net_CheckIP2::check_ip('192.168.0.1');
$ret[]=Net_CheckIP2::check_ip('192.168.1.256');
var_dump($ret);
|
あれ?IPv6は?
>A package to determine if an IP (v4) is valid.
中を見てみたところ、コンストラクタと修飾子がくっついた以外全く同じでした。
なんだこれ。
ArrayAccessインターフェイスを実装すると、クラス内の要素に配列形式でアクセスできます。
http://jp.php.net/manual/ja/class.arrayaccess.php
これを使って何が便利かというと、通常出来ない値セット時の値チェックが行えることです。
PHPの利点の一つがアバウトな型付けではありますが、しかしどうしても厳密な型を使用したい場合もあります。
そんなときにJava並の強い型付けを行える配列を作成することができます。
数値のみを格納したい配列があった場合、
$int[0]=10;
$int[1]='10';
と$int[1]に代入した時点で型エラーを出すことができます。
これまで入力値は毎回チェックしてから変数に代入なんてことをやっていたわけですが、ArrayAccessを使えば、とりあえず値を突っ込んでエラーが返ってきたら駄目だったといった判断ができるようになります。
まあ、結局例外キャッチしないといけないので手間はあんまり変わらないような気もしますが。
とりあえずint型しか使用できない配列っぽいクラスを作成してみます。
intArray.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
|
class intArray implements ArrayAccess{
//配列置き
private $arr=array();
//コンストラクタ
function __Construct(){}
//値セット
function offsetSet($a,$b){
//引数が数値かチェック
if(is_int($b)){
//[]でセットした時用
if($a===null){
$this->arr[]=$b;
}else{
$this->arr[$a]=$b;
}
}else{
//数値でなければエラー
$debug=debug_backtrace();
throw new InvalidArgumentException(
'Value is not integer in line <'.$debug[0]['line'].'>'
.' type <'.gettype($b).'>'
);
}
}
//値が存在するか
function offsetExists($a){
return isset($this->arr[$a]);
}
//値を取得
function offsetGet($a){
if($this->offsetExists($a)){
return $this->arr[$a];
}else{
return null;
}
}
//値削除
function offsetUnset($a){
if($this->offsetExists($a)){
unset($this->arr[$a]);
}
}
#↓クラスのおわり
}
|
ArrayAccessインターフェイスに存在する4つのメソッドの実装を行いました。
ArrayAccess::offsetSetのときに、引数が数値かどうかチェックを行い数値でなければInvalidArgumentExceptionを出しています。
UnexpectedValueExceptionとどっちを使えばいいのかよくわからない。
このクラスは一旦newした後、普通に配列として使用できます。
intArray.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//インスタンス
require_once('intArray.class.php');
$int=new intArray();
//代入
try{
$int[]=123;
$int['abc']=456;
$int[]='789'; //ここでInvalidArgumentExceptionが発生
}catch(InvalidArgumentException $e){
print($e->getMessage());
}
|
11行目で$int[]='789'と文字列を代入しているので、ここで例外が発生します。
それ以外は普通に配列表記でアクセスできます。
逆に$int->abcと指定しても値を取得することができません。
$int->arr['abc']としないといけないので、ここは改善の余地がありそうです。
またIteratorは実装していないので普通の配列のようにforeachでアクセスすることはできません。
以上で数値しか入れられない配列ができました。
数値以外にも列挙型なんかも簡単に作れるでしょう。
このような配列を用意しておくと、入力値チェックとかを毎回手動で行わなくて済むので簡単かもしれません。
キューもスタックもデータ型の一種で、先に入れた値が先に出てくるのがキュー、後で入れた値が先に出てくるのがスタックです。
キューが底のない筒で、スタックがコップのように底のあるものと考えるとわかりやすいでしょう。
PHPにとってはどちらも配列の一種で、array_push()、array_pop()あたりを使えば簡単に実装できます。
queue.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
|
//キュー
class Queue implements Iterator{
//配列置き
protected $arr=array();
//イテレータ
protected $loop=0;
//コンストラクタ
function __Construct(){}
//エンキュー
function enqueue($val){
array_push($this->arr,$val);
}
//デキュー
function dequeue(){
return array_shift($this->arr);
}
//イテレータ
function current(){
return $this->arr[$this->loop];
}
function next(){
$this->loop++;
}
function key(){
return $this->loop;
}
function rewind(){
$this->loop=0;
}
function valid(){
if(isset($this->arr[$this->loop])){
return true;
}else{
return false;
}
}
#↓クラスのおわり
}
//スタック
class Stack extends Queue{
//push
function push($val){
array_unshift($this->arr,$val);
}
//pop
function pop(){
return array_shift($this->arr);
}
#↓クラスのおわり
}
|
例によってエラー処理は作ってません。
キューを作ってしまえばスタックはほぼ同じなのでスタックもついでに作成しました。
入れる順番変えるだけですし。
ただこういう書き方をした場合、スタックからでもキューのメソッドを使えてしまえるのであまりよくありません。
共通のメソッドを集めた基底クラスを作ってそこからextendsしたほうがいいでしょう。
むしろどちらとしても使えるクラスが一個あれば十分という気もしないでもないですが。
使用時は普通にpush、pop、enqueue、decueするだけです。簡単。
queue.php
1
2
3
4
5
6
7
8
9
10
11
12
|
require_once('queue.class.php');
$stack=new Stack();
$stack->push(5);
$stack->push(4);
$stack->push(3);
$stack->pop();
foreach($stack as $key=>$val){
var_dump($val);
}
|
さて、PHP5.3でキュー、スタッククラスが実装されました。
http://jp.php.net/manual/ja/class.splstack.php
http://jp.php.net/manual/ja/class.splqueue.php
どう見てもSplQueue extends SplStackとしか思えない。
あと、PHPの配列って元々キューなのであまり意味がない気もしないでもない。