PHP源码之date函数

date函数的实现在源码目录的 ext/date。

1.定义函数的参数表:

ZEND_BEGIN_ARG_INFO_EX(arginfo_date, 0, 0, 1)
	ZEND_ARG_INFO(0, format)
	ZEND_ARG_INFO(0, timestamp)
ZEND_END_ARG_INFO()

将其展开,得到

#define ZEND_ARG_INFO(pass_by_ref, name)       { #name, 0, pass_by_ref, 0},
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)	\
	static const zend_internal_arg_info name[] = { \
		{ (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 },

typedef unsigned int  uintptr_t; //c语言针对不同平台定义不同,解决(void *)指针转换
typedef uintptr_t zend_uintptr_t; 
typedef unintptr_t zend_type;
typedef unsigned char zend_uchar;
typedef unsigned char zend_bool;




typedef struct _zend_internal_arg_info {
	const char *name;
	zend_type type;
	zend_uchar pass_by_reference;
	zend_bool is_variadic;
} zend_internal_arg_info;

static const zend_internal_arg_info arginfo_date[] = { 
        { (const char*)(zend_uintptr_t)1, 0, 0, 0 },   //第一个数1表示参数个数最小值,即必选参数
        { "format", 0,0, 0},
        { "timestamp", 0,0, 0},
}

 

2.声明扩展的函数列表(date函数在该扩展中):

处理函数声明:

#define PHP_FUNCTION			ZEND_FUNCTION /*在源码根目录的main/php.h中*/
#define ZEND_FN(name) zif_##name
#define ZEND_NAMED_FUNCTION(name)		void ZEND_FASTCALL name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name)				ZEND_NAMED_FUNCTION(ZEND_FN(name))

//在ext/date/php_date.h中声明函数
PHP_FUNCTION(date);


// zend_execute_data真实数据结构,可理解为当前执行的上下文环境
struct _zend_execute_data {
	const zend_op       *opline;           /* executed opline             */
	zend_execute_data   *call;             /* current call                   */
	zval                *return_value;
	zend_function       *func;             /* executed function            */
	zval                 This;             /* this + call_info + num_args    */
	zend_execute_data   *prev_execute_data;
	zend_array          *symbol_table;     
#if ZEND_EX_USE_RUN_TIME_CACHE
	void               **run_time_cache;   /* cache op_array->run_time_cache */
#endif
};

typedef struct _zend_execute_data    zend_execute_data;
#define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value


/*
*ZEND_FASTCALL 宏暂且不管
*execute_data是执行时环境数据,return_value是返回变量指针
*展开宏后得到最终函数声明
*/
void zif_date(zend_execute_data *execute_data, zval *return_value);

扩展中的函数结构定义:

#define ZEND_FN(name) zif_##name
#define ZEND_FENTRY(zend_name, name, arg_info, flags)	{ #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags },
#define ZEND_FE(name, arg_info)	ZEND_FENTRY(name,ZEND_FN(name),arg_info,0)
#define PHP_FE			ZEND_FE

/* zend_internal_function_handler */
typedef void (ZEND_FASTCALL *zif_handler)(INTERNAL_FUNCTION_PARAMETERS);

typedef struct _zend_function_entry {
	const char *fname;//函数名
	zif_handler handler;//函数句柄
	const struct _zend_internal_arg_info *arg_info;//参数列表
	uint32_t num_args;
	uint32_t flags;
} zend_function_entry;


/*
*最终展开为
*/
static const zend_function_entry date_functions[] = {
	//忽略其他
    { 
        "date", //函数名
        zif_date, //处理函数
        arginfo_date, //参数列表
        2,//参数数量最大值
        0 
    },
	//忽略其他
}

3.处理函数定义

在ext/date/php_date.c中

/* {{{ proto string date(string format [, int timestamp])
   Format a local date/time */
PHP_FUNCTION(date)
{
	php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}

/*
*展开为
*/
void zif_date(zend_execute_data *execute_data, zval *return_value)
{
    
    php_date(execute_data, return_value, 1);
}

接下来查看php_date函数






static void php_date(zend_execute_data *execute_data, zval *return_value, int localtime)
{
	zend_string *format;
	zend_long    ts;

	ZEND_PARSE_PARAMETERS_START(1, 2)
		Z_PARAM_STR(format)
		Z_PARAM_OPTIONAL
		Z_PARAM_LONG(ts)
	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

	if (ZEND_NUM_ARGS() == 1) {
		ts = time(NULL);
	}

	RETURN_STR(php_format_date(ZSTR_VAL(format), ZSTR_LEN(format), ts, localtime));
}

分别对宏展开,最终为

typedef enum _zend_expected_type {
	Z_EXPECTED_LONG
	Z_EXPECTED_BOOL
	Z_EXPECTED_STRING
	Z_EXPECTED_ARRAY
	Z_EXPECTED_FUNC
	Z_EXPECTED_RESOURCE
	Z_EXPECTED_PATH
	Z_EXPECTED_OBJECT
	Z_EXPECTED_DOUBLE
	Z_EXPECTED_LAST
} zend_expected_type;


timelib_time* timelib_time_ctor(void)
{
	timelib_time *t;
	t = timelib_calloc(1, sizeof(timelib_time));

	return t;
}



/* {{{ date_format - (gm)date helper */
static zend_string *date_format(char *format, size_t format_len, timelib_time *t, int localtime)
{
	smart_str            string = {0};
	size_t               i;
	int                  length = 0;
	char                 buffer[97];
	timelib_time_offset *offset = NULL;
	timelib_sll          isoweek, isoyear;
	int                  rfc_colon;
	int                  weekYearSet = 0;

	if (!format_len) {
		return ZSTR_EMPTY_ALLOC();
	}

	if (localtime) {
		if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
			offset = timelib_time_offset_ctor();
			offset->offset = (t->z + (t->dst * 3600));
			offset->leap_secs = 0;
			offset->is_dst = t->dst;
			offset->abbr = timelib_strdup(t->tz_abbr);
		} else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
			offset = timelib_time_offset_ctor();
			offset->offset = (t->z);
			offset->leap_secs = 0;
			offset->is_dst = 0;
			offset->abbr = timelib_malloc(9); /* GMT±xxxx\0 */
			snprintf(offset->abbr, 9, "GMT%c%02d%02d",
			                          (offset->offset < 0) ? '-' : '+',
			                          abs(offset->offset / 3600),
			                          abs((offset->offset % 3600) / 60));
		} else {
			offset = timelib_get_time_zone_info(t->sse, t->tz_info);
		}
	}

	for (i = 0; i < format_len; i++) {
		rfc_colon = 0;
		switch (format[i]) {
			/* day */
			case 'd': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->d); break;
			case 'D': length = slprintf(buffer, sizeof(buffer), "%s", php_date_short_day_name(t->y, t->m, t->d)); break;
			case 'j': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->d); break;
			case 'l': length = slprintf(buffer, sizeof(buffer), "%s", php_date_full_day_name(t->y, t->m, t->d)); break;
			case 'S': length = slprintf(buffer, sizeof(buffer), "%s", english_suffix(t->d)); break;
			case 'w': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_day_of_week(t->y, t->m, t->d)); break;
			case 'N': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_iso_day_of_week(t->y, t->m, t->d)); break;
			case 'z': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_day_of_year(t->y, t->m, t->d)); break;

			/* week */
			case 'W':
				if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
				length = slprintf(buffer, sizeof(buffer), "%02d", (int) isoweek); break; /* iso weeknr */
			case 'o':
				if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
				length = slprintf(buffer, sizeof(buffer), "%d", (int) isoyear); break; /* iso year */

			/* month */
			case 'F': length = slprintf(buffer, sizeof(buffer), "%s", mon_full_names[t->m - 1]); break;
			case 'm': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->m); break;
			case 'M': length = slprintf(buffer, sizeof(buffer), "%s", mon_short_names[t->m - 1]); break;
			case 'n': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->m); break;
			case 't': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_days_in_month(t->y, t->m)); break;

			/* year */
			case 'L': length = slprintf(buffer, sizeof(buffer), "%d", timelib_is_leap((int) t->y)); break;
			case 'y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->y % 100); break;
			case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break;

			/* time */
			case 'a': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "pm" : "am"); break;
			case 'A': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "PM" : "AM"); break;
			case 'B': {
				int retval = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
				if (retval < 0) {
					retval += 864000;
				}
				/* Make sure to do this on a positive int to avoid rounding errors */
				retval = (retval / 864)  % 1000;
				length = slprintf(buffer, sizeof(buffer), "%03d", retval);
				break;
			}
			case 'g': length = slprintf(buffer, sizeof(buffer), "%d", (t->h % 12) ? (int) t->h % 12 : 12); break;
			case 'G': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->h); break;
			case 'h': length = slprintf(buffer, sizeof(buffer), "%02d", (t->h % 12) ? (int) t->h % 12 : 12); break;
			case 'H': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->h); break;
			case 'i': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->i); break;
			case 's': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->s); break;
			case 'u': length = slprintf(buffer, sizeof(buffer), "%06d", (int) floor(t->us)); break;
			case 'v': length = slprintf(buffer, sizeof(buffer), "%03d", (int) floor(t->us / 1000)); break;

			/* timezone */
			case 'I': length = slprintf(buffer, sizeof(buffer), "%d", localtime ? offset->is_dst : 0); break;
			case 'P': rfc_colon = 1; /* break intentionally missing */
			case 'O': length = slprintf(buffer, sizeof(buffer), "%c%02d%s%02d",
											localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
											localtime ? abs(offset->offset / 3600) : 0,
											rfc_colon ? ":" : "",
											localtime ? abs((offset->offset % 3600) / 60) : 0
							  );
					  break;
			case 'T': length = slprintf(buffer, sizeof(buffer), "%s", localtime ? offset->abbr : "GMT"); break;
			case 'e': if (!localtime) {
					      length = slprintf(buffer, sizeof(buffer), "%s", "UTC");
					  } else {
						  switch (t->zone_type) {
							  case TIMELIB_ZONETYPE_ID:
								  length = slprintf(buffer, sizeof(buffer), "%s", t->tz_info->name);
								  break;
							  case TIMELIB_ZONETYPE_ABBR:
								  length = slprintf(buffer, sizeof(buffer), "%s", offset->abbr);
								  break;
							  case TIMELIB_ZONETYPE_OFFSET:
								  length = slprintf(buffer, sizeof(buffer), "%c%02d:%02d",
												((offset->offset < 0) ? '-' : '+'),
												abs(offset->offset / 3600),
												abs((offset->offset % 3600) / 60)
										   );
								  break;
						  }
					  }
					  break;
			case 'Z': length = slprintf(buffer, sizeof(buffer), "%d", localtime ? offset->offset : 0); break;

			/* full date/time */
			case 'c': length = slprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
							                (int) t->y, (int) t->m, (int) t->d,
											(int) t->h, (int) t->i, (int) t->s,
											localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
											localtime ? abs(offset->offset / 3600) : 0,
											localtime ? abs((offset->offset % 3600) / 60) : 0
							  );
					  break;
			case 'r': length = slprintf(buffer, sizeof(buffer), "%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d",
							                php_date_short_day_name(t->y, t->m, t->d),
											(int) t->d, mon_short_names[t->m - 1],
											(int) t->y, (int) t->h, (int) t->i, (int) t->s,
											localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
											localtime ? abs(offset->offset / 3600) : 0,
											localtime ? abs((offset->offset % 3600) / 60) : 0
							  );
					  break;
			case 'U': length = slprintf(buffer, sizeof(buffer), "%lld", (timelib_sll) t->sse); break;

			case '\\': if (i < format_len) i++; /* break intentionally missing */

			default: buffer[0] = format[i]; buffer[1] = '\0'; length = 1; break;
		}
		smart_str_appendl(&string, buffer, length);
	}

	smart_str_0(&string);

	if (localtime) {
		timelib_time_offset_dtor(offset);
	}

	return string.s;
}

PHPAPI zend_string *php_format_date(char *format, size_t format_len, time_t ts, int localtime) 
{
	timelib_time   *t;
	timelib_tzinfo *tzi;
	zend_string *string;

	t = timelib_time_ctor();

	if (localtime) {
		tzi = get_timezone_info();
		t->tz_info = tzi;
		t->zone_type = TIMELIB_ZONETYPE_ID;
		timelib_unixtime2local(t, ts);
	} else {
		tzi = NULL;
		timelib_unixtime2gmt(t, ts);
	}

	string = date_format(format, format_len, t, localtime);

	timelib_time_dtor(t);
	return string;
}




static void php_date(zend_execute_data *execute_data, zval *return_value, int localtime)
{
	zend_string *format;
	zend_long    ts;
/*
*ZEND_PARSE_PARAMETERS_START(1, 2)
*		Z_PARAM_STR(format)
*		Z_PARAM_OPTIONAL
*		Z_PARAM_LONG(ts)
*	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
*
*/
/*-----------------------展开开始-----------------------------*/
    do { 
		const int _flags = (0); 
		int _min_num_args = (1); 
		int _max_num_args = (2); 
		int _num_args = (execute_data)->This.u2.num_args; 
		int _i;
		zval *_real_arg, *_arg = NULL; 
		zend_expected_type _expected_type = Z_EXPECTED_LONG; //0
		char *_error = NULL; 
		zend_bool _dummy; 
		zend_bool _optional = 0; 
		int error_code = ZPP_ERROR_OK; //0
		((void)_i); 
		((void)_real_arg); 
		((void)_arg); 
		((void)_expected_type); 
		((void)_error); 
		((void)_dummy); 
		((void)_optional); 
		
		do { 
			if (UNEXPECTED(_num_args < _min_num_args) || 
			    (UNEXPECTED(_num_args > _max_num_args) && 
			     EXPECTED(_max_num_args >= 0))) { //UNEXPECTED,EXPECTED 做编译优化
				if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { 
					if (_flags & ZEND_PARSE_PARAMS_THROW) { 
						zend_wrong_parameters_count_exception(_min_num_args, _max_num_args); 
					} else { 
						zend_wrong_parameters_count_error(_min_num_args, _max_num_args); 
					} 
				} 
				error_code = ZPP_ERROR_FAILURE; 
				break; 
			} 
			_i = 0; 
			_real_arg = ZEND_CALL_ARG(execute_data, 0);



            /**********************参数:格式format********************/

            //Z_PARAM_STR(format)

            ++_i; 
	        ZEND_ASSERT(_i <= _min_num_args || _optional==1); 
	        ZEND_ASSERT(_i >  _min_num_args || _optional==0); 
	        if (_optional) { 
	        	if (UNEXPECTED(_i >_num_args)) break; 
	        } 
	        _real_arg++; 
	        _arg = _real_arg; 

		    if (UNEXPECTED(!zend_parse_arg_str(_arg, &format, 0))) { 
		    	_expected_type = Z_EXPECTED_STRING; 
		    	error_code = ZPP_ERROR_WRONG_ARG; 
		    	break; 
		    }

            _optional = 1;



            /**********************参数:时间戳ts,可选********************/

            ++_i; 
	        ZEND_ASSERT(_i <= _min_num_args || _optional==1); 
	        ZEND_ASSERT(_i >  _min_num_args || _optional==0); 
	        if (_optional) { 
	        	if (UNEXPECTED(_i >_num_args)) break; 
	        } 
	        _real_arg++; 
	        _arg = _real_arg; 
	        if (UNEXPECTED(!zend_parse_arg_long(_arg, &dest, &is_null, check_null, 0)))         
            { 
		    	_expected_type = Z_EXPECTED_LONG; 
		    	error_code = ZPP_ERROR_WRONG_ARG; 
		    	break; 
		    }

        } while (0); 
		if (UNEXPECTED(error_code != ZPP_ERROR_OK)) { 
			if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { 
				if (error_code == ZPP_ERROR_WRONG_CALLBACK) { 
					if (_flags & ZEND_PARSE_PARAMS_THROW) { 
						zend_wrong_callback_exception(_i, _error); 
					} else { 
						zend_wrong_callback_error(_i, _error); 
					} 
				} else if (error_code == ZPP_ERROR_WRONG_CLASS) { 
					if (_flags & ZEND_PARSE_PARAMS_THROW) { 
						zend_wrong_parameter_class_exception(_i, _error, _arg); 
					} else { 
						zend_wrong_parameter_class_error(_i, _error, _arg); 
					} 
				} else if (error_code == ZPP_ERROR_WRONG_ARG) { 
					if (_flags & ZEND_PARSE_PARAMS_THROW) { 
						zend_wrong_parameter_type_exception(_i, _expected_type, _arg); 
					} else { 
						zend_wrong_parameter_type_error(_i, _expected_type, _arg); 
					} 
				} 
			} 
			failure; 
		} 
	} while (0)
    /*-------------------------展开结束-----------------------------*/

	if ((execute_data)->This.u2.num_args== 1) {
		ts = time(NULL);
	}

	RETURN_STR(php_format_date(ZSTR_VAL(format), ZSTR_LEN(format), ts, localtime));
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值