php define的第二个参数使用方法

看手册说define定义的常量只允许:
仅允许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也能够定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的发生。
今天阅读php源码,发现define的第二个参数其实也可以是一个对象。
先贴一段示例:


代码如下:

class A {
    public function __toString() {
        return 'bar';
    }
}

$a = new A();
define('foo', $a);
echo foo;
// 输出bar

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


代码如下:

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

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

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

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

// 获取真正的值,用val保存
repeat:
    switch (Z_TYPE_P(val)) {
        case IS_LONG:
        case IS_DOUBLE:
        case IS_STRING:
        case IS_BOOL:
        case IS_RESOURCE:
        case IS_NULL:
            break;
        case IS_OBJECT:
            if (!val_free) {
                if (Z_OBJ_HT_P(val)->get) {
                    val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
                    goto repeat;
                } else if (Z_OBJ_HT_P(val)->cast_object) {
                    ALLOC_INIT_ZVAL(val_free);
                    if (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS) {
                        val = val_free;
                        break;
                    }
                }
            }
            /* no break */
        default:
            zend_error(E_WARNING,"Constants may only evaluate to scalar values");
            if (val_free) {
                zval_ptr_dtor(&val_free);
            }
            RETURN_FALSE;
    }

// 构建常量
    c.value = *val;
    zval_copy_ctor(&c.value);
    if (val_free) {
        zval_ptr_dtor(&val_free);
    }
    c.flags = case_sensitive; /* non persistent */
    c.name = zend_strndup(name, name_len);
    c.name_len = name_len+1;
    c.module_number = PHP_USER_CONSTANT;

// 注册常量
    if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {
        RETURN_TRUE;
    } else {
        RETURN_FALSE;
    }
}

注意以repeat开始的一段循环,还用到了goto语句T_T
这段代码的作用为:
对于int,float,string,bool,resource,null,则实际定义的常量时直接使用这些值
对于object,则需要将object转成上述6个类型之一(如果转型之后依然是object,则继续转型)
如何将object成6个类型之一呢?从代码上看有2种手段:


代码如下:

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

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 int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
{
    zval *retval;
    zend_class_entry *ce;

switch (type) {
        case IS_STRING:
            ce = Z_OBJCE_P(readobj);

// 如果用户的class中定义了__toString,则尝试调用
            if (ce->__tostring &&
                (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
                ……

}
            return FAILURE;
        ……
    }
    return FAILURE;
}

从上述具体实现来看,默认的cast_object就是去寻找class中的__tostring方法然后调用…
回到刚开始的例子,define(‘foo', $a) ,由于$a是A的实例,并且class A中定义了__toString,因此实际上foo常量就等于toString的返回值bar。

(0)

相关推荐

  • 探讨php define()函数及defined()函数使用详解

    The define() function defines a constant.define()函数的作用是:定义一个常量. Constants are much like variables, except for the following differences: 常量[constant]与变量[variable]有很多相似的地方,因此,很容易混淆:下面,我们列举一下常量[constant]与变量[variable]之间的不同点:    A constant's value cannot

  • php中define用法实例

    本文实例讲述了php中define用法.分享给大家供大家参考.具体如下: <?php /* * define * */ /*第3个参数: * false -- 对大小写敏感(默认) * true -- 对大小写不敏感 * */ define("pai",3.14); define ("name","LiLei",true); function testfor($x) { for($a=$x;$a<=12;$a++) { echo &q

  • 解析php中const与define的应用区别

    1.const用于类成员变量定义,一旦定义且不能改变其值.define定义全局常量,在任何地方都可以访问.2.define不能在类中定义而const可以.3.const不能在条件语句中定义常量 复制代码 代码如下: if (...) {     const FOO = 'BAR';    // invalid } but if (...) {     define('FOO', 'BAR'); // valid } 4.const采用一个普通的常量名称,define可以采用表达式作为名称. 复制

  • 解析php中static,const与define的使用区别

    define部分:宏不仅可以用来代替常数值,还可以用来代替表达式,甚至是代码段.(宏的功能很强大,但也容易出错,所以其利弊大小颇有争议.)宏的语法为:#define 宏名称 宏值作为一种建议和一种广大程序员共同的习惯,宏名称经常使用全部大写的字母.利用宏的优点:1)让代码更简洁明了当然,这有赖于你为宏取一个适当的名字.一般来说,宏的名字更要注重有明确直观的意义,有时宁可让它长点.2)方便代码维护对宏的处理,在编译过程中称为"预处理".也就是说在正式编译前,编译器必须先将代码出现的宏,用

  • ThinkPHP模板判断输出Defined标签用法详解

    ThinkPHP模板引擎的defined标签用于判断常量是否已经被定义. ThinkPHP的defined标签用来判断常量是否已经被定义,其功能相当于PHP中的defined()函数.defined标签用法如下: <present name="常量">要输出的内容</present> 先在模块操作(如:Index/display )里定义一个常量并输出模板: define("SITE_NAME", "我们"); $this

  • 深入php define()函数以及defined()函数的用法详解

    The define() function defines a constant.define()函数的作用是:定义一个常量.Constants are much like variables, except for the following differences: 常量[constant]与变量[variable]有很多相似的地方,因此,很容易混淆:下面,我们列举一下常量[constant]与变量[variable]之间的不同点: •A constant's value cannot be

  • php define的第二个参数使用方法

    看手册说define定义的常量只允许:仅允许标量和 null.标量的类型是 integer, float,string 或者 boolean. 也能够定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的发生.今天阅读php源码,发现define的第二个参数其实也可以是一个对象.先贴一段示例: 复制代码 代码如下: class A {    public function __toString() {        return 'bar';    }} $a = new

  • jQuery()方法的第二个参数详解

    关于jQuery()方法的第二个参数,有下面这几种用法: 1.jQuery(selector, [context]) 这种用法,相当于 $(context).find(selector) 或者 context.find(selector) 2.jQuery(html, [ownerDocument]) 文档对ownerDocument的解释是:"创建DOM元素所在的文档" 也就是说,如果你要编写挎document的脚本,比如iframe或者用window.open开一个新窗口,可能会用

  • Javascript中字符串replace方法的第二个参数探究

    前言 replace第一个参数一般放置的是正则表达式,用来匹配想要替换的文本:第二个参数一般我们放入的是字符串,用来替换掉正则匹配到的文本. 其实replace远比上面说的要强大的多,它的内部已经封装的很完善了,远不是我们想象的那么简单,下面我们就来详细聊聊replace的第二个参数. 实例介绍 现在要实现一个功能,把HTML中的字符实体转换成它们所对应的字符,比如:"<"转换成对对应字符为:"<". 下面我们来看看代码实现: String.protot

  • 基于express中路由规则及获取请求参数的方法

    express中常见的路由规则 主要使用的路由规则是get和post两种,即 var express = require('express'); var app = express(); app.get(); // get和post两种请求方式 app.post(); app.get()和app.post()的第一个参数为请求路径,第二个参数为处理请求的回调函数:回调函数有两个参数,分别为req和res,代表请求信息和响应信息. 获取请求路径和请求体中的各种参数 路径请求及对应获取请求路径的形式

  • PHP实现动态获取函数参数的方法示例

    本文实例讲述了PHP实现动态获取函数参数的方法.分享给大家供大家参考,具体如下: PHP 在用户自定义函数中支持可变数量的参数列表.其实很简单,只需使用 func_num_args() , func_get_arg() ,和 func_get_args()  函数即可. 可变参数并不需要特别的语法,参数列表仍按函数定义的方式传递给函数,并按通常的方式使用这些参数. 1. func_num_args - 返回传入函数的参数总个数 int func_num_args ( void ) 示例 <?ph

  • 为Retrofit统一添加post请求的默认参数的方法

    最近在学习使用Retrofit,并尝试将之引入到现有的项目中来.大家都知道,在Http请求中我们使用 Content-Type 来指定不同格式的请求信息: APP_FORM_URLENCODED("application/x-www-form-urlencoded"), APP_JSON("application/json"), APP_OCTET_STREAM("application/octet-stream"), MULTIPART_FOR

  • Linux Shell 如何获取参数的方法

    $# 是传给脚本的参数个数 $0 是脚本本身的名字 $1 是传递给该shell脚本的第一个参数 $2 是传递给该shell脚本的第二个参数 $@ 是传给脚本的所有参数的列表 $* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个 $$ 是脚本运行的当前进程ID号 $? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误 区别:$@, $* 相同点:都是引用所有参数 不同点:$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以&

  • 使用JPA传递参数的方法

    目录 JPA传递参数的方法 第一种 第二种 浅谈JPA优缺点 一.JPA的理解 二.JPA 实体生命周期理解和总结 三.为什么要使用JPA? JPA传递参数的方法 第一种     /**      *  通过年龄来查询数据.      *  使用@Query来配置自己定义的SQL,后面的参数nativeQuery = true才是表明使用了原生的sql      *  如果不配置,默认为false,那么将使用HQL查询方式.      *      * @param age      * @re

  • AngularJS入门教程二:在路由中传递参数的方法分析

    本文实例讲述了AngularJS在路由中传递参数的方法.分享给大家供大家参考,具体如下: 我们不仅可以在控制器中直接定义属性的值,比如: app.controller('listController',function($scope){ $scope.name="ROSE"; }); AngularJS还提供了传递参数的功能,目前我接触到的一种方式是从视图中传参: <!--首页html--> <li><a href="#/user/18"

  • java中的可变参数使用方法

    java中的可变参数使用方法 可变参数时Java 1.5新增的方法,可变参数方法接收0个或者多个指定类型的参数,可变参数机制通过先创建一个数组,数组的大小为在调用位置所传递的参数数量,然后将参数值传到数组中,最后将数组传递给方法.如: public class Test{ public int sum(int... args) { int sum = 0; for (int arg : args) { sum += arg; } return sum; } } 可变参数提供了方便,但是使用可变参

随机推荐