Zend\Uri\Uriは、URLなどを論理的に作成・分析できますよというクラスです。
APIを呼び出す際のURLを論理的に作成したり、ユーザ入力値の検証などに使えるでしょう。
今回は'http'なのでZend\Uri\Httpクラスのインスタンスとなります。
他にも色々あるかと思いきや、実は'mailto'のZend\Uri\Mailto、'file'のZend\Uri\Fileしかありません。
FTPすら無いとはびっくりだ。
ところでこれがDIなんですかね、いまいちわかりません。
normalize()やmakeRelative()はsetという名前は付いていませんがオブジェクトを直接書き換えるので、以後getScheme()やgetPath()などの値が変わる可能性があります。
少しだけ気をつけましょう。
このようにZend\Uri\Uriを使うことで、URLの論理的な組み立て、分解が可能になりました。
APIを呼び出す際のURLを論理的に作成したり、ユーザ入力値の検証などに使えるでしょう。
<?php
require_once('path/to/channel/vendor/autoload.php');
// HTTP
$http = 'http://example.com/foo/../bar.php?a=1&b=2#hoge';
$uri = Zend\Uri\UriFactory::factory($http);
// 分析
// 正しいURLか
$uri->isValid(); // true
// 正しい相対パスか
$uri->isValidRelative(); // false
// 絶対パスか
$uri->isAbsolute(); // true
// スキーマを取得
$uri->getScheme(); // http
// ユーザ名/パスワードを取得
$uri->getUserInfo(); // NULL
// ホスト名を取得
$uri->getHost(); // example.com
// ポート番号を取得
$uri->getPort(); // 80
// パスを取得
$uri->getPath(); // /foo/../bar.php
$uri->getQuery(); // a=1&b=2
$uri->getQueryAsArray(); // array('a'=>'1', 'b'=>'2')
$uri->getFragment(); // hoge
// 参照を解消
$uri->normalize()->toString(); // http://example.com/bar.php?a=1&b=2#hoge
// ベースURIからの相対パスにする
$uri->makeRelative('http://example.com/foo/')->toString(); // bar.php?a=1&b=2#hoge
/* ../bar.php?a=1&b=2#hogeにならないのはバグな気がする */
// 作成
$uri->setScheme('https');
$uri->setUserInfo('user:pass');
$uri->setHost('example.jp');
$uri->setPort(443);
$uri->setPath('/fuga.php');
$uri->setQuery(array('c'=>'3', 'd'=>'4'));
$uri->setFragment('');
$uri->toString(); // https://user:pass@example.jp:443/fuga.php?c=3&d=4
Zend\Uri\UriFactory::factoryにチェックしたいURLを突っ込むと、適切なZend\Uri\UriInterface実装クラスが帰ってきます。今回は'http'なのでZend\Uri\Httpクラスのインスタンスとなります。
他にも色々あるかと思いきや、実は'mailto'のZend\Uri\Mailto、'file'のZend\Uri\Fileしかありません。
FTPすら無いとはびっくりだ。
ところでこれがDIなんですかね、いまいちわかりません。
normalize()やmakeRelative()はsetという名前は付いていませんがオブジェクトを直接書き換えるので、以後getScheme()やgetPath()などの値が変わる可能性があります。
少しだけ気をつけましょう。
このようにZend\Uri\Uriを使うことで、URLの論理的な組み立て、分解が可能になりました。
PR
AuraとはPHPのライブラリのひとつで、綺麗でモダンなソースで有名なのだそうです。
本当かどうかはよくわかりませんが、ソースを斜め読みしたかぎりではそんなような気がしないでもないです。
例によって日本語サイトが全く存在しないので適当に触ってみます。
まずはインストール。
composer.jsonの、require部分に以下の行を追加します。
> php composer.phar install
これでインストール完了です。
どうもComposerは、packagistというサイトに登録されているパッケージであれば、repositoriesを書かなくてもいいらしいです。
auraも登録されているので、ここに出てくるパッケージ名を記入するだけです。
ってこんな機能Composerのヘルプにあったっけ?
前インストールしたときとか全く見た覚えがないんだが。
と思ったら普通に書いてあった。
packagistには他にもaura/inputとかaura/frameworkとかありましたが、inputはまだベータ、frameworkとかは何なのかよくわかりませんでした。
よくわからないので公式の記載に従っています。
それでは早速動作テスト。
AuraPHPの記事
本当かどうかはよくわかりませんが、ソースを斜め読みしたかぎりではそんなような気がしないでもないです。
例によって日本語サイトが全く存在しないので適当に触ってみます。
まずはインストール。
composer.jsonの、require部分に以下の行を追加します。
"aura/autoload": "1.*", "aura/cli": "1.*", "aura/di": "1.*", "aura/http": "1.*", "aura/marshal": "1.*", "aura/router": "1.*", "aura/signal": "1.*", "aura/sql": "1.*", "aura/uri": "1.*", "aura/view": "1.*", "aura/web": "1.*", "aura/filter": "1.*", "aura/intl": "1.*", "aura/session": "1.*"そしてインストール。
> php composer.phar install
これでインストール完了です。
どうもComposerは、packagistというサイトに登録されているパッケージであれば、repositoriesを書かなくてもいいらしいです。
auraも登録されているので、ここに出てくるパッケージ名を記入するだけです。
ってこんな機能Composerのヘルプにあったっけ?
前インストールしたときとか全く見た覚えがないんだが。
と思ったら普通に書いてあった。
packagistには他にもaura/inputとかaura/frameworkとかありましたが、inputはまだベータ、frameworkとかは何なのかよくわかりませんでした。
よくわからないので公式の記載に従っています。
それでは早速動作テスト。
<?php
require_once('path/to/channel/vendor/autoload.php');
$auraSql = new Aura\Sql\ConnectionFactory();
$connection = $auraSql->newInstance('mysql', 'host=localhost;dbname=testdb;charset=utf8;', 'testuser', 'testpass');
$sql = 'SELECT * FROM hoge LIMIT 1';
$hoge = $connection->query($sql)->fetchAll();
簡単にできました。AuraPHPの記事
Zend\Stdlib\ErrorHandlerは、PHPエラーを極めて簡単に扱えるクラスです。
通常PHPのエラー発生時は、画面に文字列としてエラーメッセージなどが表示されてしまいます。
わりかし扱いづらいので、ついつい@とかini_set('display_errors', 0);とかやってしまいがちです。
そこでErrorHandler。
PHPエラーを吸収し、例外として扱うことが可能になります。
まあset_error_handler()のラッパーです。
ErrorHandler::start()で開始、stop()で終了してエラーを捕捉できるという非常に楽な作りとなっています。
この際だからPHPエラーとはさよならしてしまいましょう。
通常PHPのエラー発生時は、画面に文字列としてエラーメッセージなどが表示されてしまいます。
わりかし扱いづらいので、ついつい@とかini_set('display_errors', 0);とかやってしまいがちです。
そこでErrorHandler。
PHPエラーを吸収し、例外として扱うことが可能になります。
まあset_error_handler()のラッパーです。
<?php
header('Content-type: text/html; charset=UTF-8');
require_once('path/to/channel/vendor/autoload.php');
use Zend\Stdlib\ErrorHandler;
// エラーハンドラ開始
ErrorHandler::start(E_ALL | E_STRICT);
// エラーはハンドラに奪われる
print($a); // Undefined variable:a
ereg('pattern', 'string'); // Function ereg() is deprecated
// パースエラーはさすがに無理
// hogehoge // Parse error: syntax error
// 例外はキャッチしない
// throw new Exception('なんか例外');
// エラーハンドラを終了して元に戻す
$exception = ErrorHandler::stop();
// stop()の返り値はErrorExceptionクラス
print($exception->getMessage()); // Function ereg() is deprecated
print($exception->getPrevious()->getMessage()); // Undefined variable:a
// 破棄して終了する場合はstop()のかわりにclean()
ErrorHandler::clean();
ErrorHandler::start()で開始、stop()で終了してエラーを捕捉できるという非常に楽な作りとなっています。
この際だからPHPエラーとはさよならしてしまいましょう。
Tokenizer関数は、Zend Engineを用いてPHPソースコードの字句解析を行うという、いったい誰がどんな時に使用するのか全く見当が付かない関数です。
ソース。
実行結果。
このように、PHPがプログラムを実行するうえで必要なトークン単位にソースを分割してくれます。
こんなのネタ以外で使う人なんていないだろと思いきや、ソースをPHPからC++に変換するという、これまた意味の分からないプロジェクトで使われていました。
誰がどんな時に使うんだそれ。
ソース。
<?php
$source = file_get_contents(basename($_SERVER['SCRIPT_NAME']));
$token = token_get_all($source);
foreach($token as $key=>$val){
if(is_array($val)){
$token[$key][0] = token_name($val[0]);
}
}
print_r($token);
実行結果。
Array
(
[0] => Array
(
[0] => T_OPEN_TAG
[1] => <?php
[2] => 1
)
[1] => Array
(
[0] => T_WHITESPACE
[1] =>
[2] => 2
)
[2] => Array
(
[0] => T_VARIABLE
[1] => $source
[2] => 2
)
[3] => Array
(
[0] => T_WHITESPACE
[1] =>
[2] => 2
)
[4] => =
[5] => Array
(
[0] => T_WHITESPACE
[1] =>
[2] => 2
)
[6] => Array
(
[0] => T_STRING
[1] => file_get_contents
[2] => 2
)
以下も続きますが省略。このように、PHPがプログラムを実行するうえで必要なトークン単位にソースを分割してくれます。
こんなのネタ以外で使う人なんていないだろと思いきや、ソースをPHPからC++に変換するという、これまた意味の分からないプロジェクトで使われていました。
誰がどんな時に使うんだそれ。
PHPの配列は、普通の配列も連想配列も実際は順序付きMapです。
JavaであればArrayListやTreeMap等色々な配列があってややこしいのですが、PHPだと全部array()で楽ちん。
これは非常に便利なのですが、でも自由度があるぶん遅いです。
固定長配列しか扱えないかわりに高速化されたSplFixedArrayというものがありましたので使ってみます。
結果は以下のとおり。
array put: 0.25703620910645
array get: 0.061129093170166
SplFixedArray put: 0.11810994148254
SplFixedArray get: 0.13133406639099
挿入はSplFixedArrayが配列の約半分、でも取得は普通の配列のほうが早くてSplFixedArrayの約半分という微妙な結果になりました。
合計としてはSplFixedArrayに軍配があがりますが、それでも100万件で0.1秒差とかそういうレベルです。
よっぽど時間にシビアなシステムでもないかぎり、現状の配列をわざわざ差し替えるほどのものではないかなあと。
どちらかというと予め最大数を決めておきたい場合に使うとかになるでしょうか。
JavaであればArrayListやTreeMap等色々な配列があってややこしいのですが、PHPだと全部array()で楽ちん。
これは非常に便利なのですが、でも自由度があるぶん遅いです。
固定長配列しか扱えないかわりに高速化されたSplFixedArrayというものがありましたので使ってみます。
<?php
$max = 1000000;
$array = array();
$splFixedArray = new SplFixedArray($max);
$time=array();
// arrayにput
$time[1] = microtime(true);
for($i=0;$i<$max;$i++){
$array[$i] = $i;
}
$time[2] = microtime(true);
// SplFixedArrayにput
$time[3] = microtime(true);
for($i=0;$i<$max;$i++){
$splFixedArray[$i] = $i;
}
$time[4] = microtime(true);
// arrayからget
$time[5] = microtime(true);
foreach($array as $key=>$val){
$tmp = $val;
}
$time[6] = microtime(true);
// SplFixedArrayからget
$time[7] = microtime(true);
foreach($splFixedArray as $key=>$val){
$tmp = $val;
}
$time[8] = microtime(true);
いいかげんな計測方法ですがまあ気にしない。結果は以下のとおり。
array put: 0.25703620910645
array get: 0.061129093170166
SplFixedArray put: 0.11810994148254
SplFixedArray get: 0.13133406639099
挿入はSplFixedArrayが配列の約半分、でも取得は普通の配列のほうが早くてSplFixedArrayの約半分という微妙な結果になりました。
合計としてはSplFixedArrayに軍配があがりますが、それでも100万件で0.1秒差とかそういうレベルです。
よっぽど時間にシビアなシステムでもないかぎり、現状の配列をわざわざ差し替えるほどのものではないかなあと。
どちらかというと予め最大数を決めておきたい場合に使うとかになるでしょうか。
http://www.webzaru.net/php/1365/
なんか色々ともやもやする。
一番気に入らないのは$id=1を指定しても全行読み込むところですが、他にも引数$idによって返り値が違うとか、全く意味のないforがあるとか、$_enc_toをループ内で毎回呼び出してるとか、if()の後に{}が無いとか色々とびみょい。
ということで書き直した。
書き直してはみたもののやはり微妙だ。
メソッドがキャメルケースなのに内部変数がスネークケースのせい?
FROMエンコーディングがファイルを確認せずにSJIS固定のせい?
特定行取得はメソッドに織り込んでるのに範囲取得はarray_sliceとかやってるせい?
いや、そもそもニュースとかCSVじゃなくてDBで管理しろってところだ。
ちなみにfgetcsv_regは有名なのですが、そのまま使うとUndefined variable: eofのNoticeが出ます。
頭に$eof = false;とか突っ込んでおきましょう。
なんか色々ともやもやする。
一番気に入らないのは$id=1を指定しても全行読み込むところですが、他にも引数$idによって返り値が違うとか、全く意味のないforがあるとか、$_enc_toをループ内で毎回呼び出してるとか、if()の後に{}が無いとか色々とびみょい。
ということで書き直した。
class GetCsv{
/**
* CSVの全行を取得する
*
* @param String ファイル名
* @return array fgetcsvの配列
*/
public static function getCsvData( $filename, $id = false ){
if($id && is_int($id)){
return self::getCsvLine($filename, $id);
}
if ( !file_exists( $filename ) ){ return false;}
$rows = array();
$_enc_to = mb_internal_encoding();
$_enc_from = 'sjis';
$handle = fopen( $filename, 'r' );
self::fgetcsv_reg( $handle );
while ( ( $data = self::fgetcsv_reg( $handle ) ) !== false ) {
$rows[] = $data;
}
mb_convert_variables( $_enc_to, $_enc_from, $rows );
fclose( $handle );
return $rows;
}
/**
* CSVの特定行を取得する
*
* @param String ファイル名
* @param int 取得する行
* @return array fgetcsv
*/
public static function getCsvLine( $filename, $id ){
if ( !file_exists( $filename ) ){ return false;}
$_enc_to = mb_internal_encoding();
$_enc_from = 'sjis';
$handle = fopen( $filename, 'r' );
for($loop=0; $loop<$id; $loop++){
if(!self::fgetcsv_reg($handle)){
return false;
}
}
$row = self::fgetcsv_reg($handle);
mb_convert_variables( $_enc_to, $_enc_from, $row );
fclose( $handle );
return $row;
}
}
書き直してはみたもののやはり微妙だ。
メソッドがキャメルケースなのに内部変数がスネークケースのせい?
FROMエンコーディングがファイルを確認せずにSJIS固定のせい?
特定行取得はメソッドに織り込んでるのに範囲取得はarray_sliceとかやってるせい?
いや、そもそもニュースとかCSVじゃなくてDBで管理しろってところだ。
ちなみにfgetcsv_regは有名なのですが、そのまま使うとUndefined variable: eofのNoticeが出ます。
頭に$eof = false;とか突っ込んでおきましょう。
Zend\Stdlib\ArrayUtilsは、配列について少し便利なチェックや操作が行えるクラスです。
ArrayUtils::isHashTable()はややこしそうに見えますが、実は単にisListの逆です。
ArrayUtils::iteratorToArray()は特徴として、イテレータが多重になっている場合は全て多重配列に解凍してくれます。
array_merge()は数値キーが0から振り直されてしまいますが、ArrayUtils::merge()は数値キーもそのままです。
本気でちょっとだけ便利なようなユーティリティでした。
いまだにリファレンスに載ってないAPIが多すぎる。
<?php
header('Content-type: text/html; charset=UTF-8');
require_once('path/to/channel/vendor/autoload.php');
use Zend\Stdlib\ArrayUtils;
$sample = array();
$sample[2] = 1;
$sample[3] = 'foo';
$sample['hoge'] = true;
$sample['fuga'] = 'bar';
// 配列が文字列のキーを持つか(is_string) → true
$hasStringKeys = ArrayUtils::hasStringKeys($sample);
// 配列が整数のキーを持つか(is_int) → true
$hasIntegerKeys = ArrayUtils::hasIntegerKeys($sample);
// 配列が数値形式文字列のキーを持つか(is_numeric) → true
$hasNumericKeys = ArrayUtils::hasNumericKeys($sample);
// 連想配列、もしくは0から順に値が埋まっていない配列であるか → false
$isList = ArrayUtils::isList($sample);
// isListの逆、ハッシュテーブルか → true
$isHashTable = ArrayUtils::isHashTable($sample);
// in_array('foo', array(0))がtrueになるのを修正 → false
$inArray = ArrayUtils::inArray(0, $sample);
// イテレータから配列に
$iteratorToArray = ArrayUtils::iteratorToArray(new \ArrayObject($sample));
// 配列のマージ。連想配列は上書き。数値キーは振り直されず、重複は追記。
$merge = ArrayUtils::merge($sample, $sample);
ArrayUtils::isHashTable()はややこしそうに見えますが、実は単にisListの逆です。
ArrayUtils::iteratorToArray()は特徴として、イテレータが多重になっている場合は全て多重配列に解凍してくれます。
array_merge()は数値キーが0から振り直されてしまいますが、ArrayUtils::merge()は数値キーもそのままです。
本気でちょっとだけ便利なようなユーティリティでした。
いまだにリファレンスに載ってないAPIが多すぎる。
Zend\Stdlib\ArrayStackは、ArrayObjectをスタックとして扱えるようになるクラスです。
中身はArrayObjectをextendsしており、変更されているメソッドはただひとつgetIterator()だけです。
出力はこんな。
FIFOのキューがFILOのスタックになり、出力が逆になりました。
はい、これだけのクラスです。
ちなみに、extendsしているArrayObjectはPHPコアの\ArrayObjectであり、Zend\Stdlib\ArrayObjectではありません。
せっかく自前で実装したZend\Stdlib\ArrayObjectを使用していないのは何故なのでしょうかね。
中身はArrayObjectをextendsしており、変更されているメソッドはただひとつgetIterator()だけです。
<?php
header('Content-type: text/html; charset=UTF-8');
require_once('path/to/channel/vendor/autoload.php');
use Zend\Stdlib\ArrayStack;
$arrayStack = new ArrayStack();
$arrayObject = new \ArrayObject();
for($i=0;$i<5;$i++){
$arrayStack->append('val:' . $i);
$arrayObject->append('val:' . $i);
}
// ArrayObject
print('ArrayObject');
foreach($arrayObject as $key=>$val){
print('key:' . $key . ' val:' . $val);
}
// ArrayStack
print('ArrayStack');
foreach($arrayStack as $key=>$val){
print('key:' . $key . ' val:' . $val);
}
出力はこんな。
ArrayObject key:0 val:val:0 key:1 val:val:1 key:2 val:val:2 key:3 val:val:3 key:4 val:val:4 ArrayStack key:0 val:val:4 key:1 val:val:3 key:2 val:val:2 key:3 val:val:1 key:4 val:val:0
FIFOのキューがFILOのスタックになり、出力が逆になりました。
はい、これだけのクラスです。
ちなみに、extendsしているArrayObjectはPHPコアの\ArrayObjectであり、Zend\Stdlib\ArrayObjectではありません。
せっかく自前で実装したZend\Stdlib\ArrayObjectを使用していないのは何故なのでしょうかね。
Zend Framework2.1.3のZend\Stdlib\ArrayObjectはクラスの中身が普通に書いてあったのですが、2.1.4にアップデートしてみたら中身がごっそり変わってました。
Zend\Stdlib\ArrayObject.phpの中身がたったこれだけに。
PHPのバージョンが5.3.4以降であればPhpReferenceCompatibilityを、それより前であればPhpLegacyCompatibilityをZend\Stdlib\AbstractArrayObjectと定義しています。
で、直後にZend\Stdlib\AbstractArrayObjectをextendsしただけのダミーのZend\Stdlib\ArrayObjectを作っています。
この結果、PHP5.3.4以降であればnew Zend\Stdlib\ArrayObjectの親クラスとしてPhpReferenceCompatibilityが、それより前であればPhpLegacyCompatibilityが設定されます。
複数の子クラスを作って必要に応じて選択するっていうのはStrategyパターンなんかがありますが、複数の親クラスから選択するのは何パターンって言うのですかね?
さてその中身はというと、PhpReferenceCompatibilityは、前回と同じくZend Frameworkで実装されたものです。
そしてPhpLegacyCompatibilityは単に\ArrayObjectを呼んでるだけでした。
結果として、PHP5.3.4より前であればPHPデフォルトの\ArrayObjectを、5.3.4以降であればZend Frameworkで実装したPhpReferenceCompatibilityを使うということで、何故か新しいバージョンの方にバグが入ってしまっているようです。
そして適当にさわってたら逆にZend\Stdlib\ArrayObjectのバグっぽいものを見つけました。
ArrayObject::STD_PROP_LISTはプロパティにforeachでアクセスできるよというフラグです。
つまり合わさるとforeachでプロパティと配列が両方出てくることになるはずなのですが、Zend\Stdlib\ArrayObjectでは配列の方しか出ません。
たぶんPhpReferenceCompatibility::__set($key, $value)にif($this->flag == self::ARRAY_AS_PROPS)って書いてるせいで足し算できてません。
まあSTD_PROP_LIST自体もおかしいんじゃねという話もあるので、実のところいったい何が正解なのかよくわからないんですけどね。
Zend\Stdlib\ArrayObject.phpの中身がたったこれだけに。
<?php
namespace Zend\Stdlib;
if (version_compare(PHP_VERSION, '5.3.4', 'lt')) {
class_alias('Zend\Stdlib\ArrayObject\PhpLegacyCompatibility', 'Zend\Stdlib\AbstractArrayObject');
} else {
class_alias('Zend\Stdlib\ArrayObject\PhpReferenceCompatibility', 'Zend\Stdlib\AbstractArrayObject');
}
class ArrayObject extends AbstractArrayObject{}
PHPのバージョンが5.3.4以降であればPhpReferenceCompatibilityを、それより前であればPhpLegacyCompatibilityをZend\Stdlib\AbstractArrayObjectと定義しています。
で、直後にZend\Stdlib\AbstractArrayObjectをextendsしただけのダミーのZend\Stdlib\ArrayObjectを作っています。
この結果、PHP5.3.4以降であればnew Zend\Stdlib\ArrayObjectの親クラスとしてPhpReferenceCompatibilityが、それより前であればPhpLegacyCompatibilityが設定されます。
複数の子クラスを作って必要に応じて選択するっていうのはStrategyパターンなんかがありますが、複数の親クラスから選択するのは何パターンって言うのですかね?
さてその中身はというと、PhpReferenceCompatibilityは、前回と同じくZend Frameworkで実装されたものです。
そしてPhpLegacyCompatibilityは単に\ArrayObjectを呼んでるだけでした。
<?php
namespace Zend\Stdlib\ArrayObject;
use ArrayObject as PhpArrayObject;
abstract class PhpLegacyCompatibility extends PhpArrayObject{
public function __construct($input = array(), $flags = self::STD_PROP_LIST, $iteratorClass = 'ArrayIterator'){
parent::__construct($input, $flags, $iteratorClass);
}
}
結果として、PHP5.3.4より前であればPHPデフォルトの\ArrayObjectを、5.3.4以降であればZend Frameworkで実装したPhpReferenceCompatibilityを使うということで、何故か新しいバージョンの方にバグが入ってしまっているようです。
そして適当にさわってたら逆にZend\Stdlib\ArrayObjectのバグっぽいものを見つけました。
<?php
header('Content-type: text/html; charset=UTF-8');
require_once('path/to/channel/vendor/autoload.php');
// Zend\Stdlib\ArrayObject
$arrayobject = new Zend\Stdlib\ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST);
$arrayobject['a'] = 'aa';
$arrayobject->b = 'bb';
print($arrayobject->a); // NULL
print($arrayobject->['b']); // NULL
foreach($arrayobject as $key=>$val){
print($key); // aのみ
}
// \ArrayObject
$default = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS | \ArrayObject::STD_PROP_LIST);
$default['a'] = 'aa';
$default->b = 'bb';
print($default->a); // aa
print($default->['b']); // bb
foreach($default as $key=>$val){
print($key); // a,b両方
}
ArrayObject::ARRAY_AS_PROPSは配列にプロパティでアクセスできるよというフラグです。ArrayObject::STD_PROP_LISTはプロパティにforeachでアクセスできるよというフラグです。
つまり合わさるとforeachでプロパティと配列が両方出てくることになるはずなのですが、Zend\Stdlib\ArrayObjectでは配列の方しか出ません。
たぶんPhpReferenceCompatibility::__set($key, $value)にif($this->flag == self::ARRAY_AS_PROPS)って書いてるせいで足し算できてません。
まあSTD_PROP_LIST自体もおかしいんじゃねという話もあるので、実のところいったい何が正解なのかよくわからないんですけどね。
Zend\Stdlib\ArrayObjectはデフォルトの\ArrayObjectを置き換えます。
デフォルトの\ArrayObjectには多次元配列を削除できないバグがあるからそれをどうにかするよ、って言ってるっぽいんですがよくわかりません。
使ってみましょう。
Zend\Stdlib\ArrayObjectは、配列のようにループ内で自分を削除することができます。
\ArrayObjectも実は削除はできているのですが、でもそこでNoticeが発生しています。
これが修正したというバグなのでしょうか?
なお、Zend\Stdlib\ArrayObjectはPHPで実装されているだけあってデフォルトの\ArrayObjectよりは動作が遅めです。
まあ10万件のappendが0.04秒から0.08秒になる程度なのでそこまで影響はないでしょう。
それ以上増やすと速度より先にメモリ不足で落ちます。
と、ここまで書いたところで気がついたのですが、最初から開発経緯を見とけよって話です。
\ArrayObjectは、配列を突っ込んだときは配列内の値を消せないよ、という話だったようです。
デフォルトの\ArrayObjectには多次元配列を削除できないバグがあるからそれをどうにかするよ、って言ってるっぽいんですがよくわかりません。
使ってみましょう。
<?php
header('Content-type: text/html; charset=UTF-8');
require_once('path/to/channel/vendor/autoload.php');
use Zend\Stdlib\ArrayObject;
// Zend\Stdlib\ArrayObject
$arrayobject = new ArrayObject();
$arrayobject->append(array('a'));
$arrayobject->append(array('b'));
foreach($arrayobject as $key=>$val){
// ループ内で自分自身を削除できる
unset($arrayobject[$key]);
}
// \ArrayObject
$default = new \ArrayObject();
$default->append(array('a'));
$default->append(array('b'));
foreach($default as $key=>$val){
// ここでArray was modified outside object and internal position is no longer valid
unset($default[$key]);
}
Zend\Stdlib\ArrayObjectは、配列のようにループ内で自分を削除することができます。
\ArrayObjectも実は削除はできているのですが、でもそこでNoticeが発生しています。
これが修正したというバグなのでしょうか?
なお、Zend\Stdlib\ArrayObjectはPHPで実装されているだけあってデフォルトの\ArrayObjectよりは動作が遅めです。
まあ10万件のappendが0.04秒から0.08秒になる程度なのでそこまで影響はないでしょう。
それ以上増やすと速度より先にメモリ不足で落ちます。
と、ここまで書いたところで気がついたのですが、最初から開発経緯を見とけよって話です。
<?php
// Zend\Stdlib\ArrayObject
$arrayobject = new Zend\Stdlib\ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST);
$arrayobject['foo'] = array('bar'=>'baz');
unset($arrayobject['foo']['bar']);
var_dump($arrayobject['foo']); // array(0) { } // 消えている
// \ArrayObject
$default = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS | \ArrayObject::STD_PROP_LIST);
$default['foo'] = array('bar'=>'baz');
unset($default['foo']['bar']); // Notice: Indirect modification of overloaded element of ArrayObject has no effectが発生
var_dump($default['foo']); // array(1) { ["bar"]=> string(3) "baz" } // 消えてない
\ArrayObjectは、配列を突っ込んだときは配列内の値を消せないよ、という話だったようです。