闲来无事,找个文件操作函数看下, 刚好中间涉及到底层对流的处理,也算作学习前的预先了解吧
/* {{{ proto int file_put_contents(string file, mixed data [, int flags [, resource context]])
Write/Create a file with contents data and return the number of bytes written */
PHP_FUNCTION(file_put_contents)
{
php_stream *stream;
char *filename;
int filename_len;
zval *data;
int numbytes = 0;
long flags = 0;
zval *zcontext = NULL;
php_stream_context *context = NULL;
php_stream *srcstream = NULL;
char mode[3] = "wb";
//可以看出只有filename是必填参数
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "pz/|lr!", &filename, &filename_len, &data, &flags, &zcontext) == FAILURE) {
return;
}
if (Z_TYPE_P(data) == IS_RESOURCE) {
php_stream_from_zval(srcstream, &data);
}
context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
if (flags & PHP_FILE_APPEND) { //追加数据
mode[0] = 'a';
} else if (flags & LOCK_EX) { //独占锁
/* check to make sure we are dealing with a regular file */
//排它锁只作用于普通文件
if (php_memnstr(filename, "://", sizeof("://") - 1, filename + filename_len)) {
if (strncasecmp(filename, "file://", sizeof("file://") - 1)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Exclusive locks may only be set for regular files");
RETURN_FALSE;
}
}
mode[0] = 'c';
}
mode[2] = '\0';
//打开文件
stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
if (stream == NULL) {
RETURN_FALSE;
}
//如果有独占锁的话, 查看此stream是否支持
if (flags & LOCK_EX && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) {
php_stream_close(stream);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Exclusive locks are not supported for this stream");
RETURN_FALSE;
}
//note 此处对流的概念还有点陌生, 找个时间系统看一下
if (mode[0] == 'c') {
php_stream_truncate_set_size(stream, 0);
}
switch (Z_TYPE_P(data)) {
case IS_RESOURCE: {
size_t len;
//对resource类型的流进行copy
if (php_stream_copy_to_stream_ex(srcstream, stream, PHP_STREAM_COPY_ALL, &len) != SUCCESS) {
numbytes = -1;
} else {
numbytes = len;
}
break;
}
case IS_NULL:
case IS_LONG:
case IS_DOUBLE:
case IS_BOOL:
case IS_CONSTANT:
convert_to_string_ex(&data);
case IS_STRING:
if (Z_STRLEN_P(data)) {
numbytes = php_stream_write(stream, Z_STRVAL_P(data), Z_STRLEN_P(data));
if (numbytes != Z_STRLEN_P(data)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only %d of %d bytes written, possibly out of free disk space", numbytes, Z_STRLEN_P(data));
numbytes = -1;
}
}
break;
case IS_ARRAY:
if (zend_hash_num_elements(Z_ARRVAL_P(data))) {
int bytes_written;
zval **tmp;
HashPosition pos;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(data), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(data), (void **) &tmp, &pos) == SUCCESS) {
//如果元素不是字符串,即此数组不是一维数组的话
if (Z_TYPE_PP(tmp) != IS_STRING) {
SEPARATE_ZVAL(tmp);
convert_to_string(*tmp);
}
if (Z_STRLEN_PP(tmp)) {
numbytes += Z_STRLEN_PP(tmp);
//可以看出只是把数组元素的键值循环拼接写入到流了
bytes_written = php_stream_write(stream, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
if (bytes_written < 0 || bytes_written != Z_STRLEN_PP(tmp)) {
if (bytes_written < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write %d bytes to %s", Z_STRLEN_PP(tmp), filename);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only %d of %d bytes written, possibly out of free disk space", bytes_written, Z_STRLEN_PP(tmp));
}
numbytes = -1;
break;
}
}
//下一个元素
zend_hash_move_forward_ex(Z_ARRVAL_P(data), &pos);
}
}
break;
case IS_OBJECT:
if (Z_OBJ_HT_P(data) != NULL) {
zval out;
//调用对象__toString方法获取返回的字符串 写入stream
if (zend_std_cast_object_tostring(data, &out, IS_STRING TSRMLS_CC) == SUCCESS) {
numbytes = php_stream_write(stream, Z_STRVAL(out), Z_STRLEN(out));
if (numbytes != Z_STRLEN(out)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only %d of %d bytes written, possibly out of free disk space", numbytes, Z_STRLEN(out));
numbytes = -1;
}
zval_dtor(&out);
break;
}
}
default:
numbytes = -1;
break;
}
php_stream_close(stream);
if (numbytes < 0) {
RETURN_FALSE;
}
RETURN_LONG(numbytes);
}
/* }}} */