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

どうでもいい記事100選

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
手順を纏める方が時間がかかっていた事は秘密。っていうか、強調したり色をつけたりしたいんだけど手順が分かりません。。。はてな記法ムツカシイ。ヘタレですみません。_| ̄|○