欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PHP源码之date函数

程序员文章站 2024-01-28 11:51:10
...

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));
}

 

相关标签: php 源码 date