define定義を無効にする(おまけ)
先日の件ですが。
define定義を無効にする方法を作成した事で定数の登録(INSERT)や削除(DELETE)ができるようになりました。
ここまでくると欲しくなるのは、やはり更新(UPDATE)。。。って、ここまで来ると定数の意味が薄れてくる気もしますが。
頭の良い皆様なら既に気づいているかもしれませんが、前回と全く同じ方法で作業する事ができます。。。というか、前回の中に既に情報は出ていました。
(いきなりですが)「Zend/zend_hash.h」の中身を覗いてみます。
% cd /usr/local/src/ % gzip -dc php-4.4.4.tar.gz | tar xf - % cd php-4.4.4 % less -N Zend/zend_hash.h 〜 省略 〜 84 /* additions/updates/changes */ 85 ZEND_API int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag); 86 #define zend_hash_update(ht, arKey, nKeyLength, pData, nDataSize, pDest) \ 87 zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_UPDATE) 88 #define zend_hash_add(ht, arKey, nKeyLength, pData, nDataSize, pDest) \ 89 zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_ADD)
「zend_hash_add」関数は88行目でdefine定義されており、関数の実体は「zend_hash_add_or_update」関数になるように定義されています。
(前回では、ここから下に進んでいって、138行目に「zend_hash_del」関数を発見しています)
88行目の(少し)上の行を確認して欲しいのですが、86行目に「zend_hash_update」関数なるものが存在しています。
注意深く見てみると「zend_hash_update」関数と「zend_hash_add」関数の引数は全く同じである事が分かります。そうですか。
「zend_hash_add」関数で追加ができるなら、「zend_hash_updatel」関数で更新もできるのでは。。。という訳で、タコさんパッチの作成に取り掛かります。
「define」関数や(その中で呼ばれている)「zend_register_constant」関数を参考にしつつ「redefine」関数を追加してみる事に。名前は(とりあえず)適当に命名しました。
今回は参考というより、ほぼコピー&ペースト作業に近いです(関数名といくつかの文字玉を変更したくらい)。
まずは、バイナリをビルド。
% ./configure --disable-all --enable-debug % make
ビルドが完了したら、バイナリに「redefine」関数の情報が含まれていない事を確認。
strings sapi/cli/php | egrep redefine
それらしい情報は存在しないので、実際にパッチを作成。
% vi php-4.4.4_redefine.patch ここから >>--------------------------------------------------------------------- --- Zend/zend_builtin_functions.c,orig 2006-01-01 22:46:49.000000000 +0900 +++ Zend/zend_builtin_functions.c 2006-10-13 20:45:40.116828560 +0900 @@ -37,6 +37,7 @@ static ZEND_FUNCTION(each); static ZEND_FUNCTION(error_reporting); static ZEND_FUNCTION(define); +static ZEND_FUNCTION(redefine); static ZEND_FUNCTION(defined); static ZEND_FUNCTION(get_class); static ZEND_FUNCTION(get_parent_class); @@ -89,6 +90,7 @@ ZEND_FE(each, first_arg_force_ref) ZEND_FE(error_reporting, NULL) ZEND_FE(define, NULL) + ZEND_FE(redefine, NULL) ZEND_FE(defined, NULL) ZEND_FE(get_class, NULL) ZEND_FE(get_parent_class, NULL) @@ -476,6 +478,66 @@ /* }}} */ +/* {{{ proto bool redefine(string constant_name, mixed value, boolean case_sensitive=true) + Define a update constant */ +ZEND_FUNCTION(redefine) +{ + zval **var, **val, **non_cs; + int case_sensitive; + zend_constant c; + + switch(ZEND_NUM_ARGS()) { + case 2: + if (zend_get_parameters_ex(2, &var, &val)==FAILURE) { + RETURN_FALSE; + } + case_sensitive = CONST_CS; + break; + case 3: + if (zend_get_parameters_ex(3, &var, &val, &non_cs)==FAILURE) { + RETURN_FALSE; + } + convert_to_long_ex(non_cs); + if ((*non_cs)->value.lval) { + case_sensitive = 0; + } else { + case_sensitive = CONST_CS; + } + break; + default: + ZEND_WRONG_PARAM_COUNT(); + break; + } + + switch((*val)->type) { + case IS_LONG: + case IS_DOUBLE: + case IS_STRING: + case IS_BOOL: + case IS_RESOURCE: + case IS_NULL: + break; + default: + zend_error(E_WARNING,"Constants may only evaluate to scalar values"); + RETURN_FALSE; + break; + } + convert_to_string_ex(var); + + c.value = **val; + zval_copy_ctor(&c.value); + c.flags = case_sensitive; /* non persistent */ + c.name = zend_strndup((*var)->value.str.val, (*var)->value.str.len); + c.name_len = (*var)->value.str.len+1; + if (zend_update_constant(&c TSRMLS_CC) == SUCCESS) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + + /* {{{ proto bool defined(string constant_name) Check whether a constant exists */ ZEND_FUNCTION(defined) --- Zend/zend_constants.c,orig 2006-01-01 22:46:49.000000000 +0900 +++ Zend/zend_constants.c 2006-10-13 20:45:40.260806672 +0900 @@ -277,6 +277,39 @@ } +ZEND_API int zend_update_constant(zend_constant *c TSRMLS_DC) +{ + char *lowercase_name = NULL; + char *name; + int ret = SUCCESS; + +#if 0 + printf("update constant for module %d\n", c->module_number); +#endif + + if (!(c->flags & CONST_CS)) { + lowercase_name = estrndup(c->name, c->name_len); + zend_str_tolower(lowercase_name, c->name_len); + name = lowercase_name; + } else { + name = c->name; + } + + if (zend_hash_update(EG(zend_constants), name, c->name_len, (void *) c, sizeof(zend_constant), NULL)==FAILURE) { + zend_error(E_NOTICE,"Constant %s not defined", name); + free(c->name); + if (!(c->flags & CONST_PERSISTENT)) { + zval_dtor(&c->value); + } + ret = FAILURE; + } + if (lowercase_name) { + efree(lowercase_name); + } + return ret; +} + + /* * Local variables: * tab-width: 4 --- Zend/zend_constants.h,orig 2006-01-01 22:46:49.000000000 +0900 +++ Zend/zend_constants.h 2006-10-13 20:45:40.453777336 +0900 @@ -56,6 +56,7 @@ ZEND_API void zend_register_string_constant(char *name, uint name_len, char *strval, int flags, int module_number TSRMLS_DC); ZEND_API void zend_register_stringl_constant(char *name, uint name_len, char *strval, uint strlen, int flags, int module_number TSRMLS_DC); ZEND_API int zend_register_constant(zend_constant *c TSRMLS_DC); +ZEND_API int zend_update_constant(zend_constant *c TSRMLS_DC); void zend_copy_constants(HashTable *target, HashTable *sourc); void copy_zend_constant(zend_constant *c); ---------------------------------------------------------------------<< ここまで
作成したら、パッチを当ててバイナリを再ビルド。
% patch -p0 < php-4.4.4_redefine.patch patching file Zend/zend_builtin_functions.c patching file Zend/zend_constants.c patching file Zend/zend_constants.h % make distclean % ./configure --disable-all --enable-debug % make
ビルドが完了したら、バイナリに「redefine」関数の情報が含まれている事を確認。
% strings sapi/cli/php | egrep redefine redefine
追加されているのが確認できたので、関数を実行してみる。
% sapi/cli/php -r ' \ define( "HOGE", "HUGA" ); \ var_dump( HOGE ); \ redefine( "HOGE", "huga" ); \ var_dump( HOGE ); ' 結果 ---- string(4) "HUGA" string(4) "huga"
思惑通り成功〜。思考1分、作業10分くらいの手抜き作業でした。(w
よく見てみたら時刻が狂ってるなぁ。。。一体誰の仕業か。直しておかないと。