忍者ブログ
[PR]
×

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



2025/01/16 12:09 |
Zend Framework2.1.4 Zend\Uri\Uri
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


2013/04/22 23:50 | Comments(0) | PHP
AuraPHP 1.0.0 Auraをインストール
AuraとはPHPのライブラリのひとつで、綺麗でモダンなソースで有名なのだそうです。
本当かどうかはよくわかりませんが、ソースを斜め読みしたかぎりではそんなような気がしないでもないです。

例によって日本語サイトが全く存在しないので適当に触ってみます。
まずはインストール。
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の記事



2013/04/19 22:06 | Comments(0) | PHP
Zend Framework2.1.3 Zend\Stdlib\ErrorHandler
Zend\Stdlib\ErrorHandlerは、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エラーとはさよならしてしまいましょう。



2013/04/17 22:58 | Comments(0) | PHP
PHP5.4.7 PHPの字句解析を行う
Tokenizer関数は、Zend Engineを用いてPHPソースコードの字句解析を行うという、いったい誰がどんな時に使用するのか全く見当が付かない関数です。

ソース。
<?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++に変換するという、これまた意味の分からないプロジェクトで使われていました。
誰がどんな時に使うんだそれ。


2013/04/15 22:05 | Comments(0) | PHP
PHP5.4.4 SplFixedArrayで固定長配列
PHPの配列は、普通の配列も連想配列も実際は順序付きMapです。
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秒差とかそういうレベルです。
よっぽど時間にシビアなシステムでもないかぎり、現状の配列をわざわざ差し替えるほどのものではないかなあと。
どちらかというと予め最大数を決めておきたい場合に使うとかになるでしょうか。



2013/04/12 23:47 | Comments(0) | PHP
WEBZARU 「PHPでCSVデータを読み込む(文字化け対策ver)」 の感想
http://www.webzaru.net/php/1365/
なんか色々ともやもやする。
一番気に入らないのは$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;とか突っ込んでおきましょう。



2013/04/10 00:00 | Comments(0) | PHP
Zend Framework2.1.3 Zend\Stdlib\ArrayUtils
Zend\Stdlib\ArrayUtilsは、配列について少し便利なチェックや操作が行えるクラスです。

<?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が多すぎる。



2013/04/08 22:48 | Comments(0) | PHP
Zend Framework2.1.3 Zend\Stdlib\ArrayStack
Zend\Stdlib\ArrayStackは、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を使用していないのは何故なのでしょうかね。




2013/04/05 23:46 | Comments(0) | PHP
Zend Framework2.1.4 Zend\Stdlib\ArrayObject
Zend Framework2.1.3のZend\Stdlib\ArrayObjectはクラスの中身が普通に書いてあったのですが、2.1.4にアップデートしてみたら中身がごっそり変わってました。

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自体もおかしいんじゃねという話もあるので、実のところいったい何が正解なのかよくわからないんですけどね。






2013/04/03 23:07 | Comments(0) | PHP
Zend Framework2.1.3 Zend\Stdlib\ArrayObject
Zend\Stdlib\ArrayObjectはデフォルトの\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は、配列を突っ込んだときは配列内の値を消せないよ、という話だったようです。





2013/04/01 23:52 | Comments(0) | PHP

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