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