Rumah  >  Artikel  >  php教程  >  define常量

define常量

WBOY
WBOYasal
2016-06-13 11:29:271002semak imbas

看手册说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

接着来看看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>

注意以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>

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>

从上述具体实现来看,默认的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>

上面代码包含了两种情况,一是我们尝试重新定义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>

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

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

 

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:php中的foreach问题Artikel seterusnya:php设计模式之命令模式