Home > php教程 > php手册 > body text

define常量

WBOY
Release: 2016-06-13 11:29:27
Original
1006 people have browsed it

看手册说define定义的常量只允许:

仅允许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也能够定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的发生。

今天阅读php源码,发现define的第二个参数其实也可以是一个对象。

先贴一段示例:

<span class</span><span  A {
    </span><span public</span> <span function</span><span  __toString() {
        </span><span return</span> 'bar'<span ;
    }
}
</span><span $a</span> = <span new</span><span  A();
</span><span define</span>('foo', <span $a</span><span );
</span><span echo</span> foo;<br />// 输出bar
Copy after login

接着来看看php中的define究竟是如何实现的:

<span ZEND_FUNCTION(define)
{
    </span><span char</span> *<span name;
    </span><span int</span><span  name_len;
    zval </span>*<span val;
    zval </span>*val_free =<span  NULL;
    zend_bool non_cs </span>= <span 0</span><span ;
    </span><span int</span> case_sensitive =<span  CONST_CS;
    zend_constant c;

    </span><span //</span><span  接收3个参数,string,zval,bool</span>
    <span if</span> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span "</span><span sz|b</span><span "</span>, &name, &name_len, &val, &non_cs) ==<span  FAILURE) {
        </span><span return</span><span ;
    }

    </span><span //</span><span  是否大小写敏感</span>
    <span if</span><span (non_cs) {
        case_sensitive </span>= <span 0</span><span ;
    }

    </span><span //</span><span  如果define类常量,则报错</span>
    <span if</span> (zend_memnstr(name, <span "</span><span ::</span><span "</span>, <span sizeof</span>(<span "</span><span ::</span><span "</span>) - <span 1</span>, name +<span  name_len)) {
        zend_error(E_WARNING, </span><span "</span><span Class constants cannot be defined or redefined</span><span "</span><span );
        RETURN_FALSE;
    }

    </span><span //</span><span  获取真正的值,用val保存</span>
<span repeat:
    </span><span switch</span><span  (Z_TYPE_P(val)) {
        </span><span case</span><span  IS_LONG:
        </span><span case</span><span  IS_DOUBLE:
        </span><span case</span><span  IS_STRING:
        </span><span case</span><span  IS_BOOL:
        </span><span case</span><span  IS_RESOURCE:
        </span><span case</span><span  IS_NULL:
            </span><span break</span><span ;
        </span><span case</span><span  IS_OBJECT:
            </span><span if</span> (!<span val_free) {
                </span><span if</span> (Z_OBJ_HT_P(val)-><span get</span><span ) {
                    val_free </span>= val = Z_OBJ_HT_P(val)-><span get</span><span (val TSRMLS_CC);
                    </span><span goto</span><span  repeat;
                } </span><span else</span> <span if</span> (Z_OBJ_HT_P(val)-><span cast_object) {
                    ALLOC_INIT_ZVAL(val_free);
                    </span><span if</span> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ==<span  SUCCESS) {
                        val </span>=<span  val_free;
                        </span><span break</span><span ;
                    }
                }
            }
            </span><span /*</span><span  no break </span><span */</span>
        <span default</span><span :
            zend_error(E_WARNING,</span><span "</span><span Constants may only evaluate to scalar values</span><span "</span><span );
            </span><span if</span><span  (val_free) {
                zval_ptr_dtor(</span>&<span val_free);
            }
            RETURN_FALSE;
    }
    
    </span><span //</span><span  构建常量</span>
    c.value = *<span val;
    zval_copy_ctor(</span>&<span c.value);
    </span><span if</span><span  (val_free) {
        zval_ptr_dtor(</span>&<span val_free);
    }
    c.flags </span>= case_sensitive; <span /*</span><span  non persistent </span><span */                 // 如果大小写不敏感,则为0,敏感则为1</span><span 
    c.name </span>=<span  zend_strndup(name, name_len);
    c.name_len </span>= name_len+<span 1</span><span ;
    c.module_number </span>=<span  PHP_USER_CONSTANT;                           <span // 标注非内核常量,而是用户定义的常量</span>
    
    </span><span //</span><span  注册常量</span>
    <span if</span> (zend_register_constant(&c TSRMLS_CC) ==<span  SUCCESS) {
        RETURN_TRUE;
    } </span><span else</span><span  {
        RETURN_FALSE;
    }
}</span>
Copy after login

注意以repeat开始的一段循环,还用到了goto语句T_T

这段代码的作用为:

  • 对于int,float,string,bool,resource,null,则实际定义的常量时直接使用这些值
  • 对于object,则需要将object转成上述6个类型之一(如果转型之后依然是object,则继续转型)

如何将object成6个类型之一呢?从代码上看有2种手段:

<span if</span> (Z_OBJ_HT_P(val)-><span get</span><span ) {
    val_free </span>= val = Z_OBJ_HT_P(val)-><span get</span><span (val TSRMLS_CC);
    </span><span goto</span><span  repeat;
}<br />// __toString()方法会在cast_object中被调用
</span><span else</span> <span if</span> (Z_OBJ_HT_P(val)-><span cast_object) {
    ALLOC_INIT_ZVAL(val_free);
    </span><span if</span> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ==<span  SUCCESS)
    {
        val </span>=<span  val_free;
        </span><span break</span><span ;
    }
}</span>
Copy after login

1,Z_OBJ_HT_P(val)->get ,宏展开之后为(*val).value.obj.handlers->get

2,Z_OBJ_HT_P(val)->cast_object,宏展开之后为(*val).value.obj.handlers->cast_object

handlers是一个包含很多函数指针的结构体,具体定义参见_zend_object_handlers 。该结构体中的函数指针均用于操作object,比如读取/修改对象属性、获取/调用对象方法等等...get和cast_object也是其中之一。

对于一般的对象,php提供了标准的cast_object函数zend_std_cast_object_tostring,代码位于php-src/zend/zend-object-handlers.c中:

ZEND_API <span int</span> zend_std_cast_object_tostring(zval *readobj, zval *writeobj, <span int</span> type TSRMLS_DC) <span /*</span><span  {{{ </span><span */</span><span 
{
    zval </span>*<span retval;
    zend_class_entry </span>*<span ce;

    </span><span switch</span><span  (type) {
        </span><span case</span><span  IS_STRING:
            ce </span>=<span  Z_OBJCE_P(readobj);
            
            </span><span //</span><span  如果用户的class中定义了__toString,则尝试调用</span>
            <span if</span> (ce->__tostring &&<span 
                (zend_call_method_with_0_params(</span>&readobj, ce, &ce->__tostring, <span "</span><span __tostring</span><span "</span>, &retval) ||<span  EG(exception))) {
                &hellip;&hellip;
                
            }
            </span><span return</span><span  FAILURE;
        &hellip;&hellip;
    }
    </span><span return</span><span  FAILURE;
}</span>
Copy after login

从上述具体实现来看,默认的cast_object就是去寻找class中的__tostring方法然后调用...

回到刚开始的例子,('foo',

<span define</span>('PHP_INT_MAX', 1);         <span //</span><span  返回FALSE</span>

<span define</span>('FOO', 1);                 <span //</span><span  返回TRUE</span>
<span define</span>('FOO', 2);                 <span //</span><span  返回FALSE</span>
Copy after login

上面代码包含了两种情况,一是我们尝试重新定义php内核的预定义常量,比如PHP_INT_MAX,这显然会失败。第二种情况是我们曾经在代码的某个位置定义过了一个常量FOO,然后又在接下来的程序中再次定义它,这也会造成失败。因此,在编码时最好将所有需要定义的常量写在一起,以免造成name重复。

2,常量名没有限制

对其name不做任何要求,当然也不需要name是一个合法的php变量名。因此,我们可以让define的常量取一些稀奇古怪的名称。例如:

<span define</span>('>_<', 123);    <span //</span><span  返回TRUE</span>
<span echo</span> >_<;              <span // </span><span syntax error</span>
Copy after login

不过如果定义了这样的常量,是没法直接使用的,会报语法错误。正确的使用方法如下:

<span define</span>('>_<', 123);        <span //</span><span  返回TRUE</span>
<span echo</span> <span constant</span>('>_<');      <span //</span><span  输出123</span>
Copy after login

 

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Recommendations
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template