diff --git a/HISTORY b/HISTORY index c200413..c37feb9 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,7 @@ -Version 1.35 2017-02-09 +Version 1.35 2017-02-15 * logger judge log_level in function log_it_ex and log_it_ex1 + * add php extension function: fastcommon_file_put_contents Version 1.34 2017-02-06 * ini_file_reader: LOCAL_IP support CIDR addresses diff --git a/php-fastcommon/fastcommon.c b/php-fastcommon/fastcommon.c index b375b31..cfb7330 100644 --- a/php-fastcommon/fastcommon.c +++ b/php-fastcommon/fastcommon.c @@ -1,5 +1,7 @@ #include "php7_ext_wrapper.h" #include "ext/standard/info.h" +#include "ext/standard/file.h" +#include "ext/standard/flock_compat.h" #include #include #include @@ -36,11 +38,25 @@ typedef struct { int alloc; int count; LogContext *contexts; -} LoggerArray; +} PHPLoggerArray; -static LoggerArray logger_array = {0, 0, NULL}; +typedef struct { + char *filename; + int fd; +} PHPFileContext; + +typedef struct { + int alloc; + int count; + PHPFileContext *contexts; +} PHPFileArray; + +static PHPLoggerArray logger_array = {0, 0, NULL}; +static PHPFileArray file_array = {0, 0, NULL}; static zval php_error_log; +static zval php_file_put_contents; static zval *error_log_func = NULL; +static zval *file_put_contents_func = NULL; #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3) const zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0 }; @@ -69,6 +85,7 @@ const zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, N ZEND_FE(fastcommon_get_cpu_count, NULL) ZEND_FE(fastcommon_get_sysinfo, NULL) ZEND_FE(fastcommon_error_log, NULL) + ZEND_FE(fastcommon_file_put_contents, NULL) {NULL, NULL, NULL} /* Must be the last line */ }; @@ -147,11 +164,23 @@ PHP_MINIT_FUNCTION(fastcommon) PHP_MSHUTDOWN_FUNCTION(fastcommon) { if (logger_array.count > 0) { - LogContext *ctx; - LogContext *end; - end = logger_array.contexts + logger_array.count; - for (ctx=logger_array.contexts; ctx 0) { + PHPFileContext *fctx; + PHPFileContext *fend; + fend = file_array.contexts + file_array.count; + for (fctx=file_array.contexts; fctxfd >= 0) { + close(fctx->fd); + fctx->fd = -1; + } } } @@ -1042,6 +1071,214 @@ ZEND_FUNCTION(fastcommon_error_log) Z_STRVAL_P(error_log_func)); RETURN_BOOL(false); } - RETURN_BOOL(true); + } +} + +static PHPFileContext *fetch_file_context(const char *filename) +{ + PHPFileContext *ctx; + PHPFileContext *end; + + if (file_array.count == 0) { + return NULL; + } + + end = file_array.contexts + file_array.count; + for (ctx=file_array.contexts; ctxfilename, filename) == 0) { + return ctx; + } + } + return NULL; +} + +static int fc_open_file(PHPFileContext *ctx) +{ + if ((ctx->fd = open(ctx->filename, O_WRONLY | + O_CREAT | O_APPEND, 0644)) < 0) + { + logError("file: "__FILE__", line: %d, " + "open file \"%s\" to write fail, " + "errno: %d, error info: %s", __LINE__, + ctx->filename, errno, strerror(errno)); + return errno != 0 ? errno : EACCES; + } + + return 0; +} + +static PHPFileContext *alloc_file_context(const char *filename) +{ + PHPFileContext *ctx; + if (file_array.alloc <= file_array.count) { + int alloc; + int bytes; + PHPFileContext *contexts; + + alloc = file_array.alloc == 0 ? 4 : 2 * file_array.alloc; + bytes = sizeof(PHPFileContext) * alloc; + contexts = (PHPFileContext *)malloc(bytes); + if (contexts == NULL) { + logError("file: "__FILE__", line: %d, " + "malloc %d bytes fail", __LINE__, bytes); + return NULL; + } + + if (file_array.count > 0) { + memcpy(contexts, file_array.contexts, + sizeof(PHPFileContext) * file_array.count); + free(file_array.contexts); + } + file_array.contexts = contexts; + file_array.alloc = alloc; + } + + ctx = file_array.contexts + file_array.count; + ctx->filename = strdup(filename); + if (ctx->filename == NULL) { + logError("file: "__FILE__", line: %d, " + "strdup %d bytes fail", + __LINE__, (int)strlen(filename)); + return NULL; + } + file_array.count++; + + if (fc_open_file(ctx) != 0) { + return NULL; + } + return ctx; +} + +static PHPFileContext *fc_get_file_context(const char *filename) +{ + PHPFileContext *ctx; + if ((ctx=fetch_file_context(filename)) != NULL) { + if (ctx->fd < 0) { + if (fc_open_file(ctx) != 0) { + return NULL; + } + } + return ctx; + } + + return alloc_file_context(filename); +} + +static int fc_file_put_contents(const char *filename, + const char *data, const int data_len, const long flags) +{ + PHPFileContext *ctx; + int bytes; + + ctx = fc_get_file_context(filename); + if (ctx == NULL) { + return -1; + } + + if ((flags & PHP_LOCK_EX) != 0) { + bytes = fc_lock_write(ctx->fd, data, data_len); + } else { + bytes = fc_safe_write(ctx->fd, data, data_len); + } + if (bytes < 0) { + logError("file: "__FILE__", line: %d, " + "write to file %s fail, errno: %d, error info: %s", + __LINE__, filename, errno, strerror(errno)); + close(ctx->fd); + ctx->fd = -1; + } + return bytes; +} + +/* +int fastcommon_file_put_contents(string $filename , mixed $data + [, int $flags = 0, resource $context]) +return the number of bytes that were written to the file, or FALSE on failure +*/ +ZEND_FUNCTION(fastcommon_file_put_contents) +{ + int argc; + long flags; + zval *zdata; + char *filename; + zval *zcontext; + zend_size_t filename_len; + + argc = ZEND_NUM_ARGS(); + if (argc < 2) { + logError("file: "__FILE__", line: %d, " + "fastcommon_file_put_contents parameters count: %d is invalid", + __LINE__, argc); + RETURN_BOOL(false); + } + + flags = 0; + zcontext = NULL; + if (zend_parse_parameters(argc TSRMLS_CC, "sz|lz", + &filename, &filename_len, &zdata, + &flags, &zcontext) == FAILURE) + { + logError("file: "__FILE__", line: %d, " + "zend_parse_parameters fail!", __LINE__); + RETURN_BOOL(false); + } + + if ((flags == PHP_FILE_APPEND || flags == (PHP_FILE_APPEND | PHP_LOCK_EX)) + && (Z_TYPE_P(zdata) == IS_STRING) && (zcontext == NULL)) + { + int bytes; + if ((bytes=fc_file_put_contents(filename, Z_STRVAL_P(zdata), + Z_STRLEN_P(zdata), flags)) >= 0) + { + RETURN_LONG(bytes); + } else { + RETURN_BOOL(false); + } + } + + { + int result; + zval *args[4]; + zval zflags; + zval zfilename; +#if PHP_MAJOR_VERSION >= 7 + zend_string *sz_filename = NULL; + bool use_heap_filename = false; +#endif + + if (file_put_contents_func == NULL) { + file_put_contents_func = &php_file_put_contents; + INIT_ZVAL(php_file_put_contents); + ZEND_ZVAL_STRINGL(&php_file_put_contents, "file_put_contents", + sizeof("file_put_contents") - 1, 1); + } + + FASTCOMMON_INIT_ZSTRING(zfilename, filename, filename_len); + + INIT_ZVAL(zflags); + ZVAL_LONG(&zflags, flags); + + if (zcontext == NULL) { + zval ctx; + zcontext = &ctx; + INIT_ZVAL(*zcontext); + ZVAL_NULL(zcontext); + } + + args[0] = &zfilename; + args[1] = zdata; + args[1] = &zflags; + args[3] = zcontext; + result = zend_call_user_function_wrapper(EG(function_table), NULL, + file_put_contents_func, return_value, 4, args TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + FASTCOMMON_ALLOCA_FREE(filename); +#endif + if (result == FAILURE) { + logError("file: "__FILE__", line: %d, " + "call function: %s fail", __LINE__, + Z_STRVAL_P(file_put_contents_func)); + RETURN_BOOL(false); + } } } diff --git a/php-fastcommon/fastcommon.h b/php-fastcommon/fastcommon.h index 091e4dd..83b56a5 100644 --- a/php-fastcommon/fastcommon.h +++ b/php-fastcommon/fastcommon.h @@ -37,6 +37,7 @@ ZEND_FUNCTION(fastcommon_get_cpu_count); ZEND_FUNCTION(fastcommon_get_sysinfo); ZEND_FUNCTION(fastcommon_error_log); +ZEND_FUNCTION(fastcommon_file_put_contents); #ifdef __cplusplus } diff --git a/php-fastcommon/test_error_log.php b/php-fastcommon/test_error_log.php index e4e07b5..246fbb4 100644 --- a/php-fastcommon/test_error_log.php +++ b/php-fastcommon/test_error_log.php @@ -3,7 +3,7 @@ function test_fastcommon_error_log() { $start = microtime(true); - for ($i=0; $i<10240; $i++) + for ($i=0; $i<102400; $i++) { fastcommon_error_log("this is a test\n", 3, "/tmp/test.log"); fastcommon_error_log("this is a test11\n", 3, "/tmp/test1.log", FASTCOMMON_LOG_TIME_PRECISION_MSECOND); @@ -24,7 +24,7 @@ function test_fastcommon_error_log() function test_error_log() { $start = microtime(true); - for ($i=0; $i<10240; $i++) + for ($i=0; $i<102400; $i++) { error_log("this is a test\n", 3, "/tmp/test.log"); error_log("this is a test11\n", 3, "/tmp/test1.log", FASTCOMMON_LOG_TIME_PRECISION_MSECOND); @@ -43,5 +43,8 @@ function test_error_log() } test_fastcommon_error_log(); +echo "sleep ...\n"; +sleep(2); +echo "sleep done.\n"; test_error_log(); diff --git a/php-fastcommon/test_file_put_contents.php b/php-fastcommon/test_file_put_contents.php new file mode 100644 index 0000000..25e83a5 --- /dev/null +++ b/php-fastcommon/test_file_put_contents.php @@ -0,0 +1,50 @@ + 0) + { + n = write(fd, p, remain); + if (n < 0) + { + int written; + if (errno == EINTR) + { + continue; + } + + written = nbyte - remain; + return written > 0 ? written : -1; + } + + p += n; + remain -= n; + } + + return nbyte; +} + +ssize_t fc_lock_write(int fd, const char *buf, const size_t nbyte) +{ + int lock_result; + int result; + + lock_result = file_write_lock(fd); + result = fc_safe_write(fd, buf, nbyte); + if (lock_result == 0) + { + file_unlock(fd); + } + + return result; +} diff --git a/src/shared_func.h b/src/shared_func.h index 8ed0be9..034e441 100644 --- a/src/shared_func.h +++ b/src/shared_func.h @@ -624,9 +624,26 @@ bool isLeadingSpacesLine(const char *content, const char *current); */ bool isTrailingSpacesLine(const char *tail, const char *end); +/** write to file + * parameters: + * fd: the fd to write + * buf: the buffer + * nbyte: the buffer length + * return: written bytes for success, -1 when fail +*/ +ssize_t fc_safe_write(int fd, const char *buf, const size_t nbyte); + +/** lock and write to file + * parameters: + * fd: the fd to write + * buf: the buffer + * nbyte: the buffer length + * return: written bytes for success, -1 when fail +*/ +ssize_t fc_lock_write(int fd, const char *buf, const size_t nbyte); + #ifdef __cplusplus } #endif #endif -