define定義を無効にする
C言語には「undef」という命令があって「define」した内容を無効にする事ができます。
PHPにも同じ名前で「define」が存在しますが「undef」に相当する命令が見当たりませんでした。
知っている方は是非とも教えて頂きたいと思います。これからやろうとしている事が盛大に無駄になるので。(w
(個人的には)define定義をする事で情報が上書きされる心配が無くなるので便利なのですが、たまに定義を無効にしたいなぁ。。。と思う時があります。って自分だけ?
これができると、(例えば)ある瞬間だけ他の値に変更して、利用し終わったら元に戻す、という事が可能になります。ソースで示すと以下のような感じです。
<?php unset( $ORIG_DEFINE ); if( defined( "HOGE" ) == TRUE ) { // オリジナルの値を一旦、退避 $ORIG_DEFINE = HOGE; undef( "HOGE" ); } // 変更したい値に再定義する define( "HOGE", "huga" ); if( isset( $ORIG_DEFINE ) == TRUE ) { // オリジナルの値に戻す undef( "HOGE" ); define( "HOGE", $ORIG_DEFINE ); } ?>
そもそも、このような状況でdefine定義を使う事自体が正しくない(こういう風に考慮しなければならないする事自体がおかしい)という風にも考える事ができますが。
話を元に戻して、見つからないというのであれば作ってしまえ。。。という事で、実際に作成してみようと思います。
いつもだったら「TAKO.php」を活用してアレコレ悩みつつ、最終的に切ったり張ったり(笑)するのですが、今回は吉岡さんのトコで取り上げられた方法で試行錯誤してみたいと思います。
ものすごーく長くなるので、興味が無い人は(このエントリは)シカトして頂ければ、と思います。
まずは、玉(アーカイブ)を展開してディレクトリを移動。
% cd /usr/local/src/ % gzip -dc php-4.4.4.tar.gz | tar xf - % cd php-4.4.4
でもって「defined」のキーワードでディレクトリ内を検索。
このキーワードにしたのは特に意味は無くて(関数の性質上)define定義を管理している情報を(きっと)知っているだろう。。。と思ったから。
% find -type f | xargs egrep -l defined ./ext/db/php_db.h ./ext/gd/gd.c ./ext/gd/gd_ctx.c ./ext/gd/libgd/gd.c ./ext/gd/libgd/gd_png.c ./ext/gd/libgd/gd_topal.c ./ext/gd/libgd/gdcache.c ./ext/gd/libgd/gdcache.h ./ext/gd/libgd/gdkanji.c ./ext/gd/libgd/gdft.c ./ext/gd/libgd/wbmp.c ./ext/gd/libgd/wbmp.h ./ext/gd/libgd/gd_io_file.c ./ext/gd/libgd/gd_jpeg.c ./ext/gd/libgd/gdtestft.c ./ext/gd/gdcache.c ./ext/gd/gdcache.h ./ext/gd/php_gd.h ./ext/gd/gdttf.c ./ext/bz2/bz2.c ./ext/dbx/howto_extend_dbx.html ./ext/com/COM.c ./ext/com/VARIANT.c ./ext/com/conversion.h ./ext/fdf/fdf.c ./ext/ftp/ftp.c ./ext/swf/swf.c ./ext/xml/expat/internal.h ./ext/xml/expat/expat.h ./ext/xml/expat/xmlparse.c ./ext/xml/expat/xmltok.c ./ext/xml/expat/xmltok.h ./ext/xml/expat/xmltok_impl.c ./ext/xml/expat/xmlrole.c ./ext/xml/php_xml.h ./ext/cpdf/cpdf.c ./ext/cpdf/php_cpdf.h ./ext/curl/curl.c ./ext/curl/curlstreams.c ./ext/exif/test.txt ./ext/exif/exif.c ./ext/imap/php_imap.c ./ext/imap/php_imap.h ./ext/imap/config.m4 ./ext/java/config.m4 ./ext/ldap/php_ldap.h ./ext/ldap/ldap.c ./ext/ming/config.m4 ./ext/msql/php_msql.c ./ext/msql/config.m4 ./ext/oci8/php_oci8.h ./ext/oci8/config.m4 ./ext/oci8/oci8.c ./ext/odbc/php_odbc.c ./ext/odbc/php_odbc.h ./ext/odbc/php_odbc_includes.h ./ext/odbc/config.m4 ./ext/odbc/php_birdstep.h ./ext/pcre/pcrelib/doc/pcre.txt ./ext/pcre/pcrelib/pcre_maketables.c ./ext/pcre/pcrelib/pcreposix.h ./ext/pcre/pcrelib/pcrecpp.h ./ext/pcre/pcrelib/pcre_printint.src ./ext/pcre/pcrelib/pcrecpp.cc ./ext/pcre/pcrelib/pcregrep.c ./ext/pcre/pcrelib/pcre_valid_utf8.c ./ext/pcre/pcrelib/pcretest.c ./ext/pcre/pcrelib/pcre_exec.c ./ext/pcre/pcrelib/pcrecpp_unittest.cc ./ext/pcre/pcrelib/NON-UNIX-USE ./ext/pcre/pcrelib/pcre_internal.h ./ext/pcre/pcrelib/pcre.h ./ext/pcre/pcrelib/pcre_compile.c ./ext/pcre/pcrelib/pcre_tables.c ./ext/pcre/pcrelib/ChangeLog ./ext/pcre/pcrelib/ucpinternal.h ./ext/snmp/snmp.c ./ext/xslt/tests/xslt_set_scheme_handlers-002.phpt ./ext/xslt/tests/xslt_set_scheme_handlers-003.phpt ./ext/xslt/README.XSLT-BACKENDS ./ext/xslt/sablot.c ./ext/zlib/config0.m4 ./ext/zlib/zlib.c ./ext/dbase/dbase.c ./ext/dbase/dbf.h ./ext/ctype/ctype.c ./ext/ctype/php_ctype.h ./ext/mysql/php_mysql.c ./ext/mysql/libmysql/strcend.c ./ext/mysql/libmysql/strtoull.c ./ext/mysql/libmysql/net.c ./ext/mysql/libmysql/thr_mutex.c ./ext/mysql/libmysql/errmsg.c ./ext/mysql/libmysql/errmsg.h ./ext/mysql/libmysql/mf_format.c ./ext/mysql/libmysql/my_winthread.c ./ext/mysql/libmysql/strtoll.c ./ext/mysql/libmysql/longlong2str.c ./ext/mysql/libmysql/thr_alarm.h ./ext/mysql/libmysql/get_password.c ./ext/mysql/libmysql/libmysql.c ./ext/mysql/libmysql/my_tempnam.c ./ext/mysql/libmysql/config-win.h ./ext/mysql/libmysql/my_static.c ./ext/mysql/libmysql/my_static.h ./ext/mysql/libmysql/my_wincond.c ./ext/mysql/libmysql/my_pthread.c ./ext/mysql/libmysql/my_pthread.h ./ext/mysql/libmysql/global.h ./ext/mysql/libmysql/dbug.c ./ext/mysql/libmysql/dbug.h ./ext/mysql/libmysql/my_create.c ./ext/mysql/libmysql/mf_path.c ./ext/mysql/libmysql/mysql_com.h ./ext/mysql/libmysql/default.c ./ext/mysql/libmysql/mysql.h ./ext/mysql/libmysql/raid.h ./ext/mysql/libmysql/acinclude.m4 ./ext/mysql/libmysql/my_lib.c ./ext/mysql/libmysql/my_net.c ./ext/mysql/libmysql/my_net.h ./ext/mysql/libmysql/my_sys.h ./ext/mysql/libmysql/my_getwd.c ./ext/mysql/libmysql/violite.c ./ext/mysql/libmysql/violite.h ./ext/mysql/libmysql/mf_fn_ext.c ./ext/mysql/libmysql/array.c ./ext/mysql/libmysql/my_init.c ./ext/mysql/libmysql/bmove.c ./ext/mysql/libmysql/my_open.c ./ext/mysql/libmysql/strmov.c ./ext/mysql/libmysql/my_alarm.h ./ext/mysql/libmysql/my_alloc.c ./ext/mysql/libmysql/my_thr_init.c ./ext/mysql/libmysql/bmove_upp.c ./ext/mysql/libmysql/m_string.h ./ext/pcntl/pcntl.c ./ext/pgsql/pgsql.c ./ext/pgsql/mysql_users.php ./ext/posix/posix.c ./ext/session/tests/bug24592.phpt ./ext/session/mod_files.c ./ext/sockets/sockets.c ./ext/filepro/filepro.c ./ext/calendar/jewish.c ./ext/hyperwave/debug.h ./ext/hyperwave/hg_comm.c ./ext/bcmath/libbcmath/src/nearzero.c ./ext/bcmath/libbcmath/src/recmul.c ./ext/bcmath/libbcmath/COPYING.LIB ./ext/bcmath/libbcmath/configure ./ext/domxml/php_domxml.c ./ext/domxml/php_domxml.h ./ext/sysvsem/sysvsem.c ./ext/overload/overload.c ./ext/mcrypt/mcrypt.c ./ext/oracle/php_oracle.h ./ext/mime_magic/mime_magic.c ./ext/mbstring/libmbfl/LICENSE ./ext/mbstring/mbregex/COPYING.LIB ./ext/mbstring/mbregex/mbregex.c ./ext/mbstring/mbregex/mbregex.h ./ext/mbstring/README_PHP3-i18n-ja ./ext/mbstring/php_unicode.h ./ext/mbstring/mbstring.c ./ext/mbstring/mbstring.h ./ext/sybase/php_sybase_db.c ./ext/sybase_ct/php_sybase_ct.c ./ext/standard/dl.c ./ext/standard/uniqid.c ./ext/standard/dir.c ./ext/standard/dns.c ./ext/standard/dns.h ./ext/standard/php_rand.h ./ext/standard/tests/file/proc_open01.phpt ./ext/standard/tests/array/bug23581.phpt ./ext/standard/tests/image/skipif_imagetype.inc ./ext/standard/tests/image/getimagesize_swc.phpt ./ext/standard/tests/general_functions/bug32647.phpt ./ext/standard/tests/general_functions/bug29038.phpt ./ext/standard/basic_functions.c ./ext/standard/flock_compat.c ./ext/standard/flock_compat.h ./ext/standard/scanf.c ./ext/standard/http_fopen_wrapper.c ./ext/standard/parsedate.c ./ext/standard/parsedate.h ./ext/standard/parsedate.y ./ext/standard/config.m4 ./ext/standard/ftp_fopen_wrapper.c ./ext/standard/datetime.c ./ext/standard/file.c ./ext/standard/file.h ./ext/standard/php_string.h ./ext/standard/html.c ./ext/standard/fsock.c ./ext/standard/info.c ./ext/standard/filestat.c ./ext/standard/var_unserializer.re ./ext/standard/mail.c ./ext/standard/math.c ./ext/standard/microtime.c ./ext/standard/pack.c ./ext/standard/rand.c ./ext/standard/syslog.c ./ext/standard/php_dir.h ./ext/standard/strnatcmp.c ./ext/standard/array.c ./ext/standard/aggregation.c ./ext/standard/var_unserializer.c ./ext/standard/image.c ./ext/standard/string.c ./ext/standard/var_unserializer.c.orig ./ext/xmlrpc/libxmlrpc/xml_to_soap.c ./ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c ./ext/xmlrpc/libxmlrpc/system_methods.c ./ext/xmlrpc/libxmlrpc/xmlrpc.c ./ext/xmlrpc/libxmlrpc/xmlrpc.h ./ext/xmlrpc/php_xmlrpc.h ./ext/xmlrpc/xmlrpc-epi-php.c ./ext/openssl/php_openssl.h ./ext/ovrimos/ovrimos.c ./ext/msession/php_msession.h ./ext/msession/reqclient.h ./ext/msession/msession.c ./ext/mnogosearch/index.php ./NEWS ./TODO ./TSRM/tsrm_nw.c ./TSRM/TSRM.c ./TSRM/TSRM.h ./TSRM/tsrm_virtual_cwd.c ./TSRM/tsrm_virtual_cwd.h ./TSRM/tsrm_config_common.h ./Zend/zend_constants.c ./Zend/zend_API.c ./Zend/zend_ini.h ./Zend/zend_execute.c ./Zend/FlexLexer.h ./Zend/zend_language_parser.c ./Zend/zend_language_parser.h ./Zend/zend_ini_parser.c ./Zend/zend_ini_parser.h ./Zend/zend_config.w32.h ./Zend/Zend.m4 ./Zend/zend_execute_API.c ./Zend/acinclude.m4 ./Zend/zend.c ./Zend/zend.h ./Zend/zend_alloc.c ./Zend/zend_builtin_functions.c ./Zend/zend_strtod.c ./Zend/ChangeLog ./Zend/zend_multibyte.c ./Zend/zend_compile.c ./Zend/acconfig.h ./Zend/zend_multiply.h ./Zend/zend_istdiostream.h ./main/php_compat.h ./main/php_open_temporary_file.c ./main/streams.c ./main/mergesort.c ./main/rfc1867.c ./main/php.h ./main/strlcat.c ./main/strlcpy.c ./main/SAPI.c ./main/fopen_wrappers.c ./main/safe_mode.c ./main/reentrancy.c ./main/win95nt.h ./main/php_reentrancy.h ./main/main.c ./main/user_streams.c ./main/network.c ./main/output.c ./main/alloca.c ./main/php_config.h.in ./main/php_network.h ./pear/PEAR/Task/Replace.php ./pear/PEAR/Task/Postinstallscript.php ./pear/PEAR/ChannelFile.php ./pear/PEAR/Config.php ./pear/PEAR/Command/Config.php ./pear/PEAR/Command/Common.php ./pear/PEAR/Command/Install.php ./pear/PEAR/Frontend/CLI.php ./pear/PEAR/Registry.php ./pear/PEAR/Autoloader.php ./pear/PEAR/Common.php ./pear/PEAR/PackageFile/v2/Validator.php ./pear/PEAR/PackageFile/Generator/v1.php ./pear/PEAR/PackageFile/Generator/v2.php ./pear/PEAR/PackageFile/v1.php ./pear/PEAR/PackageFile/v2.php ./pear/PEAR/Command.php ./pear/packages/PEAR-1.4.9.tar ./pear/README ./pear/System.php ./pear/scripts/pearcmd.php ./pear/PEAR.php ./sapi/cgi/libfcgi/libfcgi.m4 ./sapi/cgi/libfcgi/fcgi_stdio.c ./sapi/cgi/libfcgi/fcgiapp.c ./sapi/cgi/libfcgi/include/fcgi_stdio.h ./sapi/cgi/libfcgi/include/fcgios.h ./sapi/cgi/libfcgi/include/fcgiappmisc.h ./sapi/cgi/libfcgi/include/fcgiapp.h ./sapi/cgi/libfcgi/strerror.c ./sapi/cgi/cgi_main.c ./sapi/cli/php_getopt.h ./sapi/cli/php_cli.c ./sapi/embed/php_embed.c ./sapi/nsapi/nsapi.c ./sapi/roxen/roxen.c ./sapi/servlet/README ./sapi/apache/mod_php4.c ./sapi/apache/mod_php4.h ./sapi/apache/php_apache.c ./sapi/thttpd/README ./sapi/thttpd/thttpd_patch ./sapi/thttpd/thttpd.c ./sapi/apache2handler/php_functions.c ./sapi/apache2handler/sapi_apache2.c ./sapi/apache2handler/php_apache.h ./php.ini-recommended ./build/libtool.m4 ./build/build2.mk ./aclocal.m4 ./regex/regex_extra.h ./regex/regex.3 ./regex/regex.7 ./regex/regcomp.c ./regex/WHATSNEW ./tests/lang/bug23584.phpt ./tests/lang/bug27443.phpt ./tests/lang/bug29944.phpt ./tests/lang/bug25547.phpt ./tests/lang/bug25922.phpt ./tests/lang/014.phpt ./tests/lang/016.phpt ./tests/lang/017.phpt ./win32/signal.h ./win32/php4ts.rc ./win32/glob.c ./win32/sendmail.h ./win32/php4dllts.rc ./win32/php4ts_cli.rc ./win32/readdir.c ./ltmain.sh ./php.ini-dist ./configure ./configure.in ./config.guess ./README.STREAMS ./CODING_STANDARDS ./README.QNX ./acinclude.m4 ./INSTALL ./acconfig.h
量が多すぎっ!あえなく撃沈。(w
仕方が無いので、視点を変えてバイナリから直接探してみる事に。
まずは、デバッグ・モードでビルド。
% ./configure --disable-all --enable-debug % make
ビルドが完了したら同じキーワード(defined)で検索。
% strings sapi/cli/php | egrep defined 'unserialize_callback_func' defined (%s) but not found 'unserialize_callback_func' (%s) hasn't defined the class it was called for class '%s' is undefined $undefined Class %s: Cannot inherit from undefined class %s Constant %s already defined Use of undefined constant %s - assumed '%s' Null function defined as active function defined get_defined_functions get_defined_vars get_defined_constants Cannot add internal functions to return value from get_defined_functions() Cannot add user functions to return value from get_defined_functions() Undefined variable: %s Undefined class name '%s' Call to undefined function: %s() Use of undefined constant %s - assumed '%s' Undefined property: %s Undefined index: %s Undefined offset: %ld
量が少なくなった。これなら(なんとか)あたりをつけられそう。
gdbを起動して検索結果から得られた情報の中から(関数っぽやつを)ブレーク・ポイントに設定。
% gdb sapi/cli/php (gdb) b main Breakpoint 1 at 0x8122417: file /usr/local/src/php-4.4.4/sapi/cli/php_cli.c, line 504. (gdb) b defined Function "defined" not defined. Make breakpoint pending on future shared library load? (y or [n]) (gdb) b get_defined_constants Function "get_defined_constants" not defined. Make breakpoint pending on future shared library load? (y or [n]) (gdb) q
なんという事か。。。見つからない。_| ̄|○
ここで諦める訳にはいかないので、視点を変えて他のキーワードでディレクトリ内を検索。
% find -type f | xargs egrep -l get_defined_ ./ext/standard/tests/general_functions/bug29038.phpt ./NEWS ./Zend/zend_builtin_functions.c ./Zend/ChangeLog ./Zend/zend_builtin_functions.o ./sapi/cgi/php ./sapi/cli/php
get_defined_*関数は「Zend/zend_builtin_functions.c」の中にある事が分かった。
中身を覗いてみる。
% less -N Zend/zend_builtin_functions.c 〜 省略 〜 39 static ZEND_FUNCTION(define); 40 static ZEND_FUNCTION(defined); 〜 省略 〜 69 static ZEND_FUNCTION(get_defined_constants); 〜 省略 〜 91 ZEND_FE(define, NULL) 92 ZEND_FE(defined, NULL) 〜 省略 〜 123 ZEND_FE(get_defined_constants, NULL) 〜 省略 〜 419 /* {{{ proto bool define(string constant_name, mixed value, boolean case_sensitive=true) 420 Define a new constant */ 421 ZEND_FUNCTION(define) 422 { 423 zval **var, **val, **non_cs; 424 int case_sensitive; 425 zend_constant c; 426 427 switch(ZEND_NUM_ARGS()) { 428 case 2: 429 if (zend_get_parameters_ex(2, &var, &val)==FAILURE) { 430 RETURN_FALSE; 431 } 432 case_sensitive = CONST_CS; 433 break; 434 case 3: 435 if (zend_get_parameters_ex(3, &var, &val, &non_cs)==FAILURE) { 436 RETURN_FALSE; 437 } 438 convert_to_long_ex(non_cs); 439 if ((*non_cs)->value.lval) { 440 case_sensitive = 0; 441 } else { 442 case_sensitive = CONST_CS; 443 } 444 break; 445 default: 446 ZEND_WRONG_PARAM_COUNT(); 447 break; 448 } 449 450 switch((*val)->type) { 451 case IS_LONG: 452 case IS_DOUBLE: 453 case IS_STRING: 454 case IS_BOOL: 455 case IS_RESOURCE: 456 case IS_NULL: 457 break; 458 default: 459 zend_error(E_WARNING,"Constants may only evaluate to scalar values"); 460 RETURN_FALSE; 461 break; 462 } 463 convert_to_string_ex(var); 464 465 c.value = **val; 466 zval_copy_ctor(&c.value); 467 c.flags = case_sensitive; /* non persistent */ 468 c.name = zend_strndup((*var)->value.str.val, (*var)->value.str.len); 469 c.name_len = (*var)->value.str.len+1; 470 if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) { 471 RETURN_TRUE; 472 } else { 473 RETURN_FALSE; 474 } 475 } 476 /* }}} */ 477 478 479 /* {{{ proto bool defined(string constant_name) 480 Check whether a constant exists */ 481 ZEND_FUNCTION(defined) 482 { 483 zval **var; 484 zval c; 485 486 if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &var)==FAILURE) { 487 ZEND_WRONG_PARAM_COUNT(); 488 } 489 490 convert_to_string_ex(var); 491 if (zend_get_constant((*var)->value.str.val, (*var)->value.str.len, &c TSRMLS_CC)) { 492 zval_dtor(&c); 493 RETURN_TRUE; 494 } else { 495 RETURN_FALSE; 496 } 497 } 498 /* }}} */ 〜 省略 〜 1144 /* {{{ proto array get_defined_constants(void) 1145 Return an array containing the names and values of all defined constants */ 1146 ZEND_FUNCTION(get_defined_constants) 1147 { 1148 if (ZEND_NUM_ARGS() != 0) { 1149 ZEND_WRONG_PARAM_COUNT(); 1150 } 1151 1152 array_init(return_value); 1153 zend_hash_apply_with_argument(EG(zend_constants), (apply_func_arg_t) add_constant_info, return_value TSRMLS_CC); 1154 } 1155 1156 /* }}} */
それっぽいところをピックアップしてみました。
「define」関数内の470行目で「zend_register_constant」関数を呼んでいる事が分かります。
名前から察するに、定数の登録処理をやるのだろう。。。きっと。
という訳で「zend_register_constant」関数をブレーク・ポイントに設定。
% gdb sapi/cli/php (gdb) b main Breakpoint 1 at 0x8122417: file /usr/local/src/php-4.4.4/sapi/cli/php_cli.c, line 504. (gdb) b zend_register_constant Breakpoint 2 at 0x80fcd40: file /usr/local/src/php-4.4.4/Zend/zend_constants.c, line 249.
今度は設定する事ができた。一歩前進。
実際にプログラムを起動してみる。
(gdb) r -r 'define( "HOGE", "HUGA" );' Starting program: /usr/local/src/php-4.4.4/sapi/cli/php -r 'define( "HOGE", "HUGA" );' Breakpoint 1, main (argc=3, argv=0xbffffa74) at /usr/local/src/php-4.4.4/sapi/cli/php_cli.c:504 504 int exit_status = SUCCESS; Continuing. (gdb) c Breakpoint 2, zend_register_constant (c=0xbffff720) at /usr/local/src/php-4.4.4/Zend/zend_constants.c:249 249 char *lowercase_name = NULL; (gdb) bt #0 zend_register_constant (c=0xbffff720) at /usr/local/src/php-4.4.4/Zend/zend_constants.c:249 #1 0x080fcb09 in zend_register_long_constant (name=0x8147932 "E_ERROR", name_len=8, lval=1, flags=3, module_number=0) at /usr/local/src/php-4.4.4/Zend/zend_constants.c:177 #2 0x080fc7a2 in zend_register_standard_constants () at /usr/local/src/php-4.4.4/Zend/zend_constants.c:101 #3 0x081074a1 in zend_startup (utility_functions=0xbffff800, extensions=0x0, start_builtin_functions=1) at /usr/local/src/php-4.4.4/Zend/zend.c:510 #4 0x080d06fa in php_module_startup (sf=0x81549c0, additional_modules=0x0, num_additional_modules=0) at /usr/local/src/php-4.4.4/main/main.c:1123 #5 0x08122569 in main (argc=3, argv=0xbffffa74) at /usr/local/src/php-4.4.4/sapi/cli/php_cli.c:582 (gdb) q The program is running. Exit anyway? (y or n) y
バックトレースの情報から関数の実体は「Zend/zend_constants.c」の中にある事が分かった。
中身を覗いてみる。
% less -N Zend/zend_constants.c 〜 省略 〜 247 ZEND_API int zend_register_constant(zend_constant *c TSRMLS_DC) 248 { 249 char *lowercase_name = NULL; 250 char *name; 251 int ret = SUCCESS; 252 253 #if 0 254 printf("Registering constant for module %d\n", c->module_number); 255 #endif 256 257 if (!(c->flags & CONST_CS)) { 258 lowercase_name = estrndup(c->name, c->name_len); 259 zend_str_tolower(lowercase_name, c->name_len); 260 name = lowercase_name; 261 } else { 262 name = c->name; 263 } 264 265 if (zend_hash_add(EG(zend_constants), name, c->name_len, (void *) c, sizeof(zend_constant), NULL)==FAILURE) { 266 zend_error(E_NOTICE,"Constant %s already defined", name); 267 free(c->name); 268 if (!(c->flags & CONST_PERSISTENT)) { 269 zval_dtor(&c->value); 270 } 271 ret = FAILURE; 272 } 273 if (lowercase_name) { 274 efree(lowercase_name); 275 } 276 return ret; 277 }
思っていたより全然そっけない。
「zend_register_constant」関数内の265行目で「zend_hash_add」関数を呼んでいる事が分かります。
引数に「EG(zend_constants)」があるので、これが定数を管理している情報なのだろう。きっと。
次のブレーク・ポイントを探す為に「zend_hash_add」関数名でバイナリファイルを検索。
% strings sapi/cli/php | egrep zend_hash_add zend_hash_add_or_update zend_hash_add_empty_element
微妙に名前が違ってる。(w
それっぽい名前である「zend_hash_add_or_update」関数でブレーク・ポイントに設定。
% gdb sapi/cli/php (gdb) b zend_hash_add_or_update Breakpoint 1 at 0x810c71a: file /usr/local/src/php-4.4.4/Zend/zend_hash.c, line 232. (gdb) q
バックトレースの情報から関数の実体は「Zend/zend_hash.c」の中にある事が分かった。
中身を覗いてみる。
% less -N Zend/zend_hash.c 〜 省略 〜 226 ZEND_API int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag)
この中には「zend_hash_add」関数が存在しないようなので、関数の実体を探す為に「zend_hash_add_or_update」のキーワードでディレクトリ内を検索。
% find -type f | xargs egrep -l zend_hash_add_or_update ./ext/standard/array.o ./ext/standard/basic_functions.o ./ext/standard/browscap.o ./ext/standard/file.o ./ext/standard/filestat.o ./ext/standard/iptc.o ./ext/standard/reg.o ./ext/standard/string.o ./ext/standard/syslog.o ./ext/standard/var.o ./ext/standard/incomplete_class.o ./ext/standard/url_scanner_ex.o ./ext/standard/http_fopen_wrapper.o ./ext/standard/var_unserializer.o ./ext/standard/aggregation.o ./Zend/zend_hash.c ./Zend/zend_hash.h ./Zend/zend_language_scanner.o ./Zend/zend_compile.o ./Zend/zend_constants.o ./Zend/zend_execute_API.o ./Zend/zend_operators.o ./Zend/zend.o ./Zend/zend_API.o ./Zend/zend_hash.o ./Zend/zend_builtin_functions.o ./Zend/zend_ini.o ./Zend/zend_execute.o ./main/php3_compat.h ./main/main.o ./main/php_ini.o ./main/SAPI.o ./main/rfc1867.o ./main/php_content_types.o
オブジェクトファイルが検索結果に沢山該当しましたが、その中に「Zend/zend_hash.h」がある事を発見。
この中を覗いてみる。
% 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) 〜 省略 〜 136 /* Deletes */ 137 ZEND_API int zend_hash_del_key_or_index(HashTable *ht, char *arKey, uint nKeyLength, ulong h, int flag); 138 #define zend_hash_del(ht, arKey, nKeyLength) \ 139 zend_hash_del_key_or_index(ht, arKey, nKeyLength, 0, HASH_DEL_KEY) 140 #define zend_hash_index_del(ht, h) \ 141 zend_hash_del_key_or_index(ht, NULL, 0, h, HASH_DEL_INDEX)
「zend_hash_add」関数は88行目でdefine定義されており、関数の実体は「zend_hash_add_or_update」関数になるように定義されています。
で、下の方に進んでいくと138行目に「zend_hash_del」関数なるものを発見。
「zend_hash_add」関数で追加ができるなら、逆に「zend_hash_del」関数で削除できるのでは。。。と、ここで(ようやく)タコさんパッチの作成ができそうだ。。。と思うようになる。
「define」関数や(その中で呼ばれている)「zend_register_constant」関数を参考にしつつ「undef」関数を追加してみる事に。
まずはバイナリに「undef」関数の情報が含まれていない事を確認。
% strings sapi/cli/php | egrep undef class '%s' is undefined $undefined Class %s: Cannot inherit from undefined class %s Use of undefined constant %s - assumed '%s' Call to undefined function: %s() Use of undefined constant %s - assumed '%s'
それらしい情報は存在しないので、実際にパッチを作成。
% vi php-4.4.4_undef.patch ここから >>--------------------------------------------------------------------- --- Zend/zend_builtin_functions.c,orig 2006-01-01 22:46:49.000000000 +0900 +++ Zend/zend_builtin_functions.c 2006-10-07 07:14:58.974185152 +0900 @@ -37,6 +37,7 @@ static ZEND_FUNCTION(each); static ZEND_FUNCTION(error_reporting); static ZEND_FUNCTION(define); +static ZEND_FUNCTION(undef); 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(undef, NULL) ZEND_FE(defined, NULL) ZEND_FE(get_class, NULL) ZEND_FE(get_parent_class, NULL) @@ -476,6 +478,50 @@ /* }}} */ +/* {{{ proto bool undef(string constant_name, boolean case_sensitive=true) + Remove defined a constant */ +ZEND_FUNCTION(undef) +{ + zval **var, **non_cs; + int case_sensitive; + zend_constant c; + + switch(ZEND_NUM_ARGS()) { + case 1: + if (zend_get_parameters_ex(1, &var)==FAILURE) { + RETURN_FALSE; + } + case_sensitive = CONST_CS; + break; + case 2: + if (zend_get_parameters_ex(2, &var, &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; + } + convert_to_string_ex(var); + + 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_remove_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-07 07:14:59.189152472 +0900 @@ -277,6 +277,35 @@ } +ZEND_API int zend_remove_constant(zend_constant *c TSRMLS_DC) +{ + char *lowercase_name = NULL; + char *name; + int ret = SUCCESS; + + 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_del(EG(zend_constants), name, c->name_len)==FAILURE) { + zend_error(E_NOTICE,"Constant %s can 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-07 07:14:59.373124504 +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_remove_constant(zend_constant *c TSRMLS_DC); void zend_copy_constants(HashTable *target, HashTable *sourc); void copy_zend_constant(zend_constant *c); ---------------------------------------------------------------------<< ここまで
「Zend/zend_builtin_functions.h」の中には、それっぽい定義が見当たらなかったので(とりあえず)放置。実際のパッチは既存の関数を流用して(数行)ちょろっと変更しただけ。
作成したら、パッチを当ててバイナリを再ビルド。
% patch -p0 < php-4.4.4_undef.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
ビルドが完了したら、バイナリに「undef」関数の情報が含まれている事を確認。
% strings sapi/cli/php | egrep undef class '%s' is undefined $undefined Class %s: Cannot inherit from undefined class %s Use of undefined constant %s - assumed '%s' undef Call to undefined function: %s() Use of undefined constant %s - assumed '%s'
追加されているのが確認できたので、関数を実行してみる。
果たして上手くいくか。。ドキドキの瞬間。
% sapi/cli/php -r ' \ define( "HOGE", "HUGA" ); \ var_dump( defined( "HOGE" ) ); \ var_dump( HOGE ); \ undef( "HOGE" ); \ var_dump( defined( "HOGE" ) ); \ define( "HOGE", "huga" ); \ var_dump( defined( "HOGE" ) ); \ var_dump( HOGE ); ' 結果 ---- bool(true) string(4) "HUGA" bool(false) bool(true) string(4) "huga"
や、やったー!成功!!ガッツ!!!(T-T
最近、本ばっかり読んでてサボってたので、久々のタコさんパッチは少し嬉しい。
変更できるようになった事による影響度は未知数。。。だったらやるなよな。
今回、PHPのバージョンを「4.4.4」にしていますが(ファイル名やディレクトリ構造は大きく変わってはいないので)PHPのバージョンを上げてもパッチの内容と同じ定義を追加してやる事で利用する事ができると思います。駄目だったら連絡して頂ければ、と思います。
パッチの作成までにかかった時間は120分くらいでした。
初めての事だったので、色々と確認・試行錯誤しつつやってたので、次はもう少し短縮できるとよいのですが。
普段やってる手順だったら、大体40分くらいかなぁ。。。慣れてるせいもあると思うけど。次回は検索精度の向上に努めたい。
タコさんパッチの真髄は「参考になりそうな情報を元に切ったり張ったりする事」なんだろうな、と改めて実感。創意工夫が全く見当たらないっ。(w
手順を纏める方が時間がかかっていた事は秘密。っていうか、強調したり色をつけたりしたいんだけど手順が分かりません。。。はてな記法ムツカシイ。ヘタレですみません。_| ̄|○