ます’s Blog - どうでもいい記事100選

どうでもいい記事100選

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
よく見てみたら時刻が狂ってるなぁ。。。一体誰の仕業か。直しておかないと。