2011年10月12日

PHP:array_key_existsより array自体がヤバいんじゃないか?

互換性のために作った array_intersect_key を見直していて、ふと不思議な文をマニュアルに見つけた。それが下の一文。

「二つの要素は、 (string) $elem1 === (string) $elem2 の場合のみ等しいとみなされます。言い替えると、厳密なチェックが行われるため、文字列表現が同じである必要があります。」
(つたない英語力だが英語サイトも同様)
ref) http://www.php.net/manual/en/function.array-intersect-key.php

一瞬「え?」です。

ここで配列の基本の幾つか。

  1. key は、整数 または 文字列です。

  2. 整数の標準的な表現形式である場合、 そのように解釈されます (つまり、"8" は 8 として解釈されます。一方、 "08" は "08" として解釈されます)。


ref) http://www.php.net/manual/ja/language.types.array.php

つまり、厳密であろうと曖昧であろうと、key は「重ならない」と思っていたわけです。つまり array(1 => value1, "1" => value2) と定義しても array(1 => value2) と解釈される。そのため array_key_exists に strict が無くても問題はなく、isset のような恐ろしく曖昧なものより信用していたんですね。それなのに array_intersect_key の場合は、string への型変換後に厳密なチェック云々とはどういうこっちゃ? と。array の性質上すでに解決済みのハズ。

で、この前書いた。
PHP:array_key_exists のバグ?
http://zombiebook.seesaa.net/article/228944224.html
がまざまざと蘇る訳です。
しょーがなので、イヤイヤ試すと案の定といいますか……。
(※ 今回は Windows, php 5.2.4 のみで検査です)

array(
 '2147483647' => int 0,
 2147483647 => int 0,
);

あらまあやっちゃってるよ。コノ人。上記2のルールが解決されてない。そう思うと array_key_exists には strict だとどこにも書いてないが、strict だとしたらそっちのほうが正しい動きをしていた訳だ。むしろ array が悪い子じゃん。まあ、内部的にいつ解釈されるのか知りませんが、var_dump の結果です。

本来なら。
array(
 2147483647 => int 0,
);
になるはず。

これ放置するのオカシイよね? こんな基本的な事ホントに既知じゃないの? 5.2 までスルー? それとも私が何か読み落としてる?

既知かどうか、色々調べようかと思ったけど……。
なんだかどうでも良くなってきた。ハァ……。php 嫌いじゃないんだけどねぇ。なんだか型宣言が必要な言語が懐かしくなってきたよ。

ちなみに1
int max は設定できる。これを設定しなおすとどうなるか不明。

ちなみに2
キーが想定できない場合 $ary[$key] で走査する部分は危険かもしれない。具体的には for や foreach よりも each でペアを取った方が安全。もう調べる気力も試す気力もないが。

個人的に以下はエラーにすべきだと。検査してはいるが存在自体気分悪い。
key に浮動小数点数値を指定すると、 その値は integer に切り詰められます。キーとして TRUE を使用した場合、 整数型の 1 がキーとして解釈されます。 キーとして FALSE を使用した場合、 整数型の 0 がキーとして解釈されます。 キーとして NULL を使用した場合、 空の文字列として評価されます。




posted by HiFa at 00:31 | 愛知 ☁ | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2011年10月06日

PHP:array_key_exists のバグ?

array_key_exists('2147483647', array(2147483647 => 1));

が true にならない……。(バグ?)
知っての通り array_key_exists は strict ではない。
ということで、2147483647 から 1 を引いた

array_key_exists('2147483646', array(2147483646 => 1));

では true になる。(期待通り)
そこで、面白いことに

array_key_exists('-2147483647', array(-2147483647 => 1));

も true。(期待通り)
これは 32bit 版固有の問題で、整数型の取りうる範囲が-2147483647〜2147483647までだから、「2147483647」という値に起こったのだと思う。これは既知のバグなのだろうか? 徹夜の追い込み中で調べる気力がないので、ここでメモw

一応調査した環境は
CPI Linux, php 5.1.6
Xserver Linux, php 5.1.6
XREA Linux, php 5.2.5
Windows, php 5.2.4

SAKURA FreeBSD, php 5.2.17

ただし! 最後の SAKURA は期待通り true になった! ふぅ。なんじゃそりゃ^^;;; php 5.3以上ではどうだろう。誰か知ってる人コメください。
ところで整数型で -2147483648 を作るには……

var_dump(-2147483647); // int -2147483647 <-- int 扱い
var_dump(-2147483648); // float -2147483648 <-- float 扱い
var_dump(1073741824 << 1); // int -2147483648 <-- int になったw
posted by HiFa at 05:10 | 愛知 ☔ | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2010年04月10日

php:関数を再帰的に利用できる関数

配列に対して関数を再帰的に適用させる関数は幾つかありますが、どれも使い勝手が悪い。array_walk_recursive では与え値は1つだけで、コールバックに渡されるのは「配列の値、配列の添字、与え値」と言う感じで、組み込み関数にも適用しづらい。コールバックには「処理対象、オプション1、オプション2・・・」って渡す方が使いやすい。

recursive('mb_convert_encoding', $ary, 'UTF-8', 'SJIS');
recursive('trim', $ary);

とか出来ればラクだよなー。
なので与え値可変長で、関数を再帰的に利用する関数を作ってみました。

ちなみに array_map_recursive も巷で見かけるけど array_map 自体が配列の処理が前提で、与え値にも配列を要求するので再帰処理として面倒(じゃない?)
他、使えそうなのって無かったよなぁ・・・。探す前に作ってしまうのは、仕事ってより骨休めの趣味なんだろなぁ。

「結果の違い」で結果の表記を間違っていましたので直しました。(同日修正)
続きを読む
posted by HiFa at 14:23 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2010年01月24日

php:${ }って結構便利よ

うっかりプログラミング日誌さん
ReflectionClassでメソッドを動的に呼ぶ&ベンチマーク
http://d.hatena.ne.jp/gom68/20090926/1253939933

あまり、記事内容に沿うリンクじゃありませんが、ReflectionClass を調査していてふと・・・。

続きを読む
posted by HiFa at 15:29 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2009年01月14日

php:インスタンスのクラスを変えてしまう

php で擬似的に以下2つ
・ インスタンスのクラスを変更
・ メソッドの動的な追加

幾つかのオブジェクトシステムではインスタンスのクラスを変えられますが、それを php で真似る。

■ まずは結果
// 変換先クラスサンプル
class foo{
  public $p = 'yo!';
  public function dump(){var_dump($this);}
}

// 変更させるインスタンス
$obj = new stdClass;
$obj->d = 'hi!';

// 変更前のvar_dump
var_dump($obj);// object(stdClass)#1 (1) { ["d"]=> string(3) "hi!" }
echo "<br><br>";

// クラスを変えてしまう
change_class(&$obj, 'foo');

// 変更後のvar_dump
$obj->dump();// object(foo)#2 (2) { ["p"]=> string(3) "yo!" ["d"]=> string(3) "hi!" }
echo "<br><br>";

// foo そのものから作ると
$foo = new foo;
var_dump($foo);// object(foo)#1 (1) { ["p"]=> string(3) "yo!" }
echo "<br><br>";

// 一見するとメソッドの動的な追加のような事も
add_method(&$obj, 'public function hello(){echo "hello!<br />";}');
$obj->hello();// hello!

// メソッド追加後の var_dump
$obj->dump();// object(add_method_0)#3 (2) { ["p"]=> string(3) "yo!" ["d"]=> string(3) "hi!" }
echo "<br><br>";


■ 自前の環境
PHP Version 5.2.3
System Windows NT
Server API Apache 2.0

続きを読む
posted by HiFa at 22:29 | 🌁 | Comment(0) | TrackBack(1) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2009年01月12日

PHP:var_dump だって見やすくなりたい(デバッグ用 var_dump)

「この程度のスクリプトでデバッグ用ライブラリなんてかったりぃなぁ。」
ってな時などにvar_dumpをより見やすくする関数。
前回、var_dump が循環参照に意外と抵抗力があるってな記事を流したんで、これに乗じて今まで使ってた関数をアップグレードしました。しかも、調子こいて色付けやら強調やらも。
var_dump()を使う前に
http://blog.cles.jp/item/1512

pre タグでもいいのですが、string データに html ドキュメントや改行があるとそれもまた見難いので、ついでにそれも変換します。
PHPでvar_dump()を使ったお手軽デバッグコード by ウェブライフハック
http://www.mapee.jp/wlh/phpvar_dump.html

まー。実のところ、いままでは var_export の出力結果を整形してたんですが、これがいっつも循環参照でひっかかる。どうせデバッグ用なので、面倒がって見て見ぬ振りしましたが、とうとう我慢し切れず前回のテストに至った訳ですw
てことで var_dump を可視化エンジン(笑)にしたオブジェクトビジュアライズ関数(!)

■ やってくれること
・ WEBページで var_dump を見やすくする
・ 重要要素には文字修飾する

■ 出力例
object(stdClass)#1 (3) {
      ["ary"]=>
      array(3) {
          [0]=>
          int(100)
          [1]=>
          int(200)
          ["key"]=>
          string(5) "value"
      }
      ["txt"]=>
      string(34) "<div>html ドキュメント</div>"
      ["obj"]=>
      object(stdClass)#2 (1) {
          ["child"]=>
          object(stdClass)#3 (3) {
              ["name"]=>
              string(6) "太郎"
              ["old"]=>
              string(6) "4歳"
              ["recursive"]=>
              object(stdClass)#1 (3) {
                  ["ary"]=>
                  array(3) {
                      [0]=>
                      int(100)
                      [1]=>
                      int(200)
                      ["key"]=>
                      string(5) "value"
                  }
                  ["txt"]=>
                  string(34) "<div>html ドキュメント</div>"
                  ["obj"]=>
                  object(stdClass)#2 (1) {
                      ["child"]=>
                      object(stdClass)#3 (3) {
                          ["name"]=>
                          string(6) "太郎"
                          ["old"]=>
                          string(6) "4歳"
                          ["recursive"]=>
                          *RECURSION*
                      }
                  }
              }
          }
      }
  }


■ 出力例の試料
$a = new stdClass();
$a->ary = array(100, 200, 'key' => 'value');
$a->txt = '<div>html ドキュメント</div>';
$a->obj = new stdClass();
$a->obj->child = new stdClass();
$a->obj->child->name = '太郎';
$a->obj->child->old = '4歳';
$a->obj->child->recursive = $a;

var_html($a); // 出力


■ 自前の環境
PHP Version 5.2.3
System Windows NT
Server API Apache 2.0

続きを読む
posted by HiFa at 01:35 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2009年01月11日

PHP:循環参照に対する動作の違い

・ 循環参照を持つ変数に何らかの処理を加えた時の動作の相違
・ 循環参照を見つける簡易関数(php5)

自前環境で、循環参照に対する幾つかの関数の動作を試行。
ちなみに、php v5.3 では循環参照をガーベッジ・コレクターが検出し、循環参照に使用されているメモリーを解放することができるとのこと。

■ 循環参照の例
A)配列で値渡し(※)
$a = array();
$a[0] = $a;

B)配列で参照渡し
$a = array();
$a[0] = &$a;

C)クラスインスタンスで値渡し
$a = new stdClass;
$a->p = $a;

D)クラスインスタンスで参照渡し
$a = new stdClass;
$a->p = &$a;


※ うーむ。これは循環参照というのかなぁ???

■ 自前の環境
PHP Version 5.2.3
System Windows NT
Server API Apache 2.0

続きを読む
posted by HiFa at 18:25 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2007年02月08日

php:バイト数の取得(strlen は mb_strlen にオーバーロードされる)

strlen が mb_strlen にオーバーロード(置換)されてしまっている場合でも支障なくデータのバイト数を取得する方法です。

環境によっては strlen は ms_strlen にオーバーロードされているんですが(マニュアル:XCIV. マルチバイト文字列関数)、そんな場合、データのバイト数を取得するのに通常はどうするのでしょう?

$volm = strlen(bin2hex($data)) / 2;

$data:バイト数を取得したいデータ
$volm:データ長(byte)

これでマルチバイトのテキストでも画像でも、バイト数を取得できます。当たり前ですが。
ちなみに関数のオーバーロードに関する設定(mbstring.func_overload)は、ユーザスクリプトからはできません。php.ini か .htaccess、httpd.conf で設定可能です。

ずっとこれで通してきてます。
んが。実は、一般的な手法を知りたかったんですが、調べてもちょっと見つからなかったので、私なりのやり方です。
うーん。普通にコア関数があるのだろうか、見落としてるハズないんだけどなぁ。
posted by HiFa at 11:54 | 🌁 | Comment(0) | TrackBack(1) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2007年01月27日

php:配列の次元数を調べる関数

配列がもつデータ(配列)のうち最大の深度を調べる関数です。
配列の各要素をスキャンして、その値が配列だった場合に、再帰的に再度スキャンします。これを繰り返して最大の深度(次元数)を調べます。

値渡し/参照渡し、どちらが有利かまでは考慮してません。php バージョンによって異なる可能性もあります。必要であれば適宜改造してください。ま、この程度のもので気にすることもないですね。巨大な配列で無い限り問題にはならないと思います。

巨大な配列の場合はアルゴリズム自体も見直す必要があります。
コードを簡易化するために関数のオーバーヘッドが、1回余分に掛かかるような処理が入ってます。など。

続きを読む
posted by HiFa at 11:02 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2006年12月18日

PHP : オブジェクトメソッドを指定する可変関数について

オブジェクトメソッドを指定する可変関数について、前回流した分の補足です。
前回はこちら

例えば、$obj->ex = 'sleep'; として $obj->ex(); としても sleep は使えないですよね。ま、当然の話しで、オブジェクトメソッドを指定しているのに、オブジェクトにそのメソッドが無い。
※ $obj に sleep というメソッドがあれば call 出来ます。

実際に書いてみたのが以下のコード。書くまでも無くエラーになりますw

続きを読む
posted by HiFa at 17:10 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

PHP4コアでメソッドの動的な追加

PHP4 コアのみで、PECLの runkit 関数を使わずにメソッドの動的な追加(っぽい事)をやってみます。

登録した関数をオブジェクト内部から呼び出す事と、メソッドの動的な生成です。

同日修正:コード修正しました。
すみません。ポカミスってました。呼び出される関数で $this に受け取っていたので、$slf へ受け取ります。

■ 2009/04/24追記
php:インスタンスのクラスを変えてしまう
こういうやり方でも擬似的に追加や変更が出来る

続きを読む
posted by HiFa at 03:55 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする

2006年12月12日

php : mbstring.encoding_translation って痛いかも?

mbstring.encoding_translation って意外と曲者

例えば php.ini で。
mbstring.internal_encoding = EUC-JP
mbstring.encoding_translation = On


なんて感じになっていると、php コードの頭に
mb_internal_encoding("UTF-8");

と指定しても POST されたデータなんかは UTF-8 にならない。
当たり前だけど、時すでに遅し。プロセスがデータ受け取る時にはすでに EUC にエンコードされちゃってる。

EUC でしかドキュメント作らねーなんて今までが平穏&ラッキー過ぎた。こんなしょーもないことでえらい事悩んでしまった・・・。

デフォじゃ off だけど、有効にしてるレンサバって多いのかなぁ。
ini_get で、mbstring.encoding_translation 見て動作変えるのって、結構重要かも。

ひっさびさの php だ。
何ヶ月ぶりだろ。このブログじゃ初だわw

今までなんとなーく EUC を使ってたんだけど、やっぱ UTF-8 っしょってな具合で重い腰を上げたんですけどね。(重すぎかぁ!?)
やっぱ場当たり的な対応で逃げてるとダメですね。文字にゃ苦労してる割に身になってない・・・。
posted by HiFa at 23:34 | 🌁 | Comment(0) | TrackBack(0) | ときどきPHP | このブログの読者になる | 更新情報をチェックする