使用自定义 PHP 模块,为什么不填充 $_POST?
Using custom PHP module, why isn't $_POST being populated?
我已经在 macOS 10.12.1 Sierra
上将 PHP 5.6.27
编译为静态库(这本身就是一个不小的壮举)。我已将我的应用程序链接到所有必需的库,并且它正在构建而没有错误。使用静态 PHP 库,我能够执行任意 PHP 代码和执行 PHP 脚本。但是,如果我尝试使用表单数据(从同一应用程序中的嵌入式 HTTP 服务器检索)处理脚本,则永远不会填充 $_POST
全局(如果请求,$_GET
全局设置得很好是一个 GET
,在 URL).
中有一个查询字符串
不包括整个项目,这里是我的 PHP 处理器 class 和我的自定义 PHP 模块和一个用于测试处理器的存根,没有实际的 HTTP 请求但具有相同的请求生成的内容是我的 class 需要的。您可以在底部看到,在 t运行 的结果中,PHP 脚本是 运行 并且正在调用 read_post 方法并正确加载提供的缓冲区 PHP 然后应该用来填充 $_POST
数组,但 $_POST
数组仍然是空的(尽管已初始化)。任何提示将不胜感激。
PHPProcessor.h:
#import <Cocoa/Cocoa.h>
@interface PHPProcessor : NSObject
@property (strong) NSString *requestMethod;
@property (strong) NSURL *requestURL;
@property (strong) NSString *requestContentType;
@property (strong) NSData *bodyData;
@property (readonly) NSString *resultString;
+ (instancetype)phpProcessor;
- (BOOL)executeScript:(NSString *)filepath;
@end
PHPProcessor.m:
#import "PHPProcessor.h"
#include "php.h"
#include "php_main.h"
#include "php_variables.h"
@interface PHPProcessor ()
@property (assign) NSInteger bodyDataOffset;
@property (strong) NSString *resultString;
@end
static int phpp_startup(sapi_module_struct *sapi_module) {
return ((php_module_startup(sapi_module, NULL, 0) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_shutdown(sapi_module_struct *sapi_module) {
return ((php_module_shutdown_wrapper(sapi_module) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_ub_write(const char *str, unsigned int str_length TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSString *newString = [NSString stringWithUTF8String:str];
if (!newString) newString = [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
if (newString) {
NSString *resultString = phpProcessor.resultString;
if (!resultString) resultString = [NSString string];
resultString = [resultString stringByAppendingString:newString];
phpProcessor.resultString = resultString;
return str_length;
}
return 0;
}
static int phpp_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
static int phpp_read_post(char *buffer, uint count_bytes TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSData *bodyData = phpProcessor.bodyData;
uint remaining_bytes = (uint)(bodyData.length - phpProcessor.bodyDataOffset);
count_bytes = MIN(count_bytes, remaining_bytes);
[bodyData getBytes:buffer range:NSMakeRange(phpProcessor.bodyDataOffset, count_bytes)];
phpProcessor.bodyDataOffset += count_bytes;
#if DEBUG
NSLog(@"verifying buffer from read_post: %@", [[NSString alloc] initWithBytes:buffer length:phpProcessor.bodyDataOffset encoding:NSASCIIStringEncoding]);
#endif
return count_bytes;
}
static void phpp_register_variable(int arg, zval *track_vars_array, const char *key, const char *val TSRMLS_DC) {
char *new_val = (char *)val; uint new_val_len;
if (val && sapi_module.input_filter(arg, (char *)key, &new_val, (unsigned int)strlen(val), &new_val_len TSRMLS_CC)) php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC);
}
static void phpp_register_server_variables(zval *track_vars_array TSRMLS_DC) {
phpp_register_variable(PARSE_SERVER, track_vars_array, "SERVER_SOFTWARE", [[[NSBundle mainBundle] objectForInfoDictionaryKey:(id)kCFBundleNameKey] UTF8String]);
}
static void phpp_log_message(char *message TSRMLS_DC) {
printf("%s\n", message);
}
sapi_module_struct phpp_module = {
"phpp", // char *name;
"PHP Processor Module", // char *pretty_name;
phpp_startup, // int (*startup)(struct _sapi_module_struct *sapi_module);
phpp_shutdown, // int (*shutdown)(struct _sapi_module_struct *sapi_module);
NULL, // int (*activate)(TSRMLS_D);
NULL, // int (*deactivate)(TSRMLS_D);
phpp_ub_write, // int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);
NULL, // void (*flush)(void *server_context);
NULL, // struct stat *(*get_stat)(TSRMLS_D);
NULL, // char *(*getenv)(char *name, size_t name_len TSRMLS_DC);
NULL, // void (*sapi_error)(int type, const char *error_msg, ...);
NULL, // int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
phpp_send_headers, // int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
NULL, // void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);
phpp_read_post, // int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC);
NULL, // char *(*read_cookies)(TSRMLS_D);
phpp_register_server_variables, // void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);
phpp_log_message, // void (*log_message)(char *message TSRMLS_DC);
NULL, // double (*get_request_time)(TSRMLS_D);
NULL, // void (*terminate_process)(TSRMLS_D);
"", // char *php_ini_path_override; // "" causes $_ENV to be set
STANDARD_SAPI_MODULE_PROPERTIES
};
@implementation PHPProcessor
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sapi_module_struct *sapi_module = &phpp_module;
sapi_startup(sapi_module);
sapi_module->startup(sapi_module);
});
}
+ (instancetype)phpProcessor { return [self new]; }
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
- (BOOL)executeScript:(NSString *)filepath {
self.resultString = nil;
self.bodyDataOffset = 0;
SG(request_info).request_method = (char *)self.requestMethod.UTF8String;
SG(request_info).content_type = (char *)self.requestContentType.UTF8String;
SG(request_info).content_length = (long)self.bodyData.length;
SG(request_info).request_uri = (char *)self.requestURL.relativePath.UTF8String;
SG(request_info).path_translated = (char *)filepath.UTF8String;
SG(request_info).query_string = (char *)self.requestURL.query.UTF8String;
if (php_request_startup(TSRMLS_C) == SUCCESS) {
SG(server_context) = (void *)CFBridgingRetain(self);
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = filepath.UTF8String;
file_handle.free_filename = 0;
file_handle.opened_path = NULL;
php_execute_script(&file_handle TSRMLS_CC);
php_request_shutdown(NULL);
SG(server_context) = NULL;
CFRelease(self);
return YES;
}
return NO;
}
@end
test.php:
<?php
echo("<pre>\n");
echo("GET: ");
print_r($_GET);
echo("POST: ");
print_r($_POST);
echo("SERVER: ");
print_r($_SERVER);
echo("ENV: ");
print_r($_ENV);
echo("</pre>\n");
phpinfo();
?>
要使用的代码PHP处理器:
NSString *filepath = @"~/Desktop/test.php".stringByExpandingTildeInPath;
PHPProcessor *phpProcessor = [PHPProcessor phpProcessor];
phpProcessor.requestMethod = @"POST";
phpProcessor.requestURL = [NSURL fileURLWithPath:filepath];
phpProcessor.requestContentType = @"application/x-www-form-urlencoded";
phpProcessor.bodyData = [@"a=123&b=456" dataUsingEncoding:NSUTF8StringEncoding];
[phpProcessor executeScript:filepath];
NSLog(@"resultString:\n\n%@", phpProcessor.resultString);
结果:
2016-10-30 17:26:10.429404 PHPProcessor Test[38190:2886757] verifying buffer from read_post: a=123&b=456
2016-10-30 17:26:10.429931 PHPProcessor Test[38190:2886757] resultString:
<pre>
GET: Array
(
)
POST: Array
(
)
SERVER: Array
(
[SERVER_SOFTWARE] => PHPProcessor Test
[REQUEST_TIME_FLOAT] => 1477862770.425
[REQUEST_TIME] => 1477862770
[argv] => Array
(
)
[argc] => 0
)
ENV: Array
(
[SHELL] => /bin/bash
...
)
</pre>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
...
<title>phpinfo()</title>
...
<body><div class="center">
<table>
<tr class="h"><td>
<a href="http://www.php.net/"><img border="0" src="data:image/png;base64,..." alt="PHP logo" /></a><h1 class="p">PHP Version 5.6.27</h1>
...
</table>
</div></body></html>
对于那些对解决方案感兴趣的人,基本上归结为将 sapi_module_struct
启动移动到 class initialization
方法之外,并从实例方法调用它。我还稍微清理了其余代码,出于某种原因,为了避免崩溃,我不得不添加一个用于读取 cookie 的函数,即使我最初对它们不感兴趣(返回一个有效的 cookie 字符串确实设置了$_COOKIE
数组)。希望这可以帮助其他人。
已修订 PHPProcessor.m:
#import "PHPProcessor.h"
#include "php.h"
#include "php_main.h"
#include "php_variables.h"
@interface PHPProcessor ()
@property (assign) NSInteger bodyDataOffset;
@property (strong) NSMutableString *mutableResultString;
@end
static int phpp_startup(sapi_module_struct *sapi_module) {
return ((php_module_startup(sapi_module, NULL, 0) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_shutdown(sapi_module_struct *sapi_module) {
return ((php_module_shutdown_wrapper(sapi_module) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_ub_write(const char *str, unsigned int str_length TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSString *newString = [NSString stringWithUTF8String:str];
if (!newString) newString = [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
if (newString) {
[phpProcessor.mutableResultString appendString:newString];
return str_length;
}
return 0;
}
static int phpp_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
static int phpp_read_post(char *buffer, uint count_bytes TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSData *bodyData = phpProcessor.bodyData;
uint remaining_bytes = (uint)(bodyData.length - phpProcessor.bodyDataOffset);
count_bytes = MIN(count_bytes, remaining_bytes);
[bodyData getBytes:buffer range:NSMakeRange(phpProcessor.bodyDataOffset, count_bytes)];
phpProcessor.bodyDataOffset += count_bytes;
#if DEBUG
NSLog(@"verifying buffer from read_post: %@", [[NSString alloc] initWithBytes:buffer length:phpProcessor.bodyDataOffset encoding:NSASCIIStringEncoding]);
#endif
return count_bytes;
}
static char *phpp_read_cookies(TSRMLS_D) {
return NULL;
}
static void phpp_register_variable(int arg, zval *track_vars_array, const char *key, const char *val TSRMLS_DC) {
char *new_val = (char *)val; uint new_val_len;
if (val && sapi_module.input_filter(arg, (char *)key, &new_val, (unsigned int)strlen(val), &new_val_len TSRMLS_CC)) php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC);
}
static void phpp_register_server_variables(zval *track_vars_array TSRMLS_DC) {
phpp_register_variable(PARSE_SERVER, track_vars_array, "SERVER_SOFTWARE", [[[NSBundle mainBundle] objectForInfoDictionaryKey:(id)kCFBundleNameKey] UTF8String]);
}
static void phpp_log_message(char *message TSRMLS_DC) {
printf("%s\n", message);
}
sapi_module_struct phpp_module = {
"phpp", // char *name;
"PHP Processor Module", // char *pretty_name;
phpp_startup, // int (*startup)(struct _sapi_module_struct *sapi_module);
phpp_shutdown, // int (*shutdown)(struct _sapi_module_struct *sapi_module);
NULL, // int (*activate)(TSRMLS_D);
NULL, // int (*deactivate)(TSRMLS_D);
phpp_ub_write, // int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);
NULL, // void (*flush)(void *server_context);
NULL, // struct stat *(*get_stat)(TSRMLS_D);
NULL, // char *(*getenv)(char *name, size_t name_len TSRMLS_DC);
php_error, // void (*sapi_error)(int type, const char *error_msg, ...);
NULL, // int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
phpp_send_headers, // int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
NULL, // void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);
phpp_read_post, // int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC);
phpp_read_cookies, // char *(*read_cookies)(TSRMLS_D);
phpp_register_server_variables, // void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);
phpp_log_message, // void (*log_message)(char *message TSRMLS_DC);
NULL, // double (*get_request_time)(TSRMLS_D);
NULL, // void (*terminate_process)(TSRMLS_D);
"", // char *php_ini_path_override; // "" causes $_ENV to be set
STANDARD_SAPI_MODULE_PROPERTIES
};
@implementation PHPProcessor
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sapi_startup(&phpp_module);
});
}
+ (instancetype)phpProcessor { return [self new]; }
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
- (void)shutdownModule {
SG(server_context) = NULL;
CFRelease(self);
php_module_shutdown(TSRMLS_C);
}
- (BOOL)executeScript:(NSString *)filepath {
self.bodyDataOffset = 0;
self.mutableResultString = [NSMutableString string];
if (phpp_module.startup(&phpp_module) == FAILURE) return NO;
int exit_status = FAILURE;
zend_first_try {
SG(server_context) = (void *)CFBridgingRetain(self);
SG(request_info).request_method = (char *)self.requestMethod.UTF8String;
SG(request_info).content_type = (char *)self.requestContentType.UTF8String;
SG(request_info).content_length = (long)self.bodyData.length;
SG(request_info).request_uri = (char *)self.requestURL.relativePath.UTF8String;
SG(request_info).path_translated = (char *)filepath.fileSystemRepresentation;
SG(request_info).query_string = (char *)self.requestURL.query.UTF8String;
char *argv[2] = {phpp_module.name, NULL};
SG(request_info).argc = 1;
SG(request_info).argv = argv;
SG(request_info).argv0 = phpp_module.name;
SG(sapi_headers).http_response_code = 200;
if (php_request_startup(TSRMLS_C) == FAILURE) {
[self shutdownModule];
return NO;
}
if (SG(request_info).path_translated) {
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = SG(request_info).path_translated;
file_handle.free_filename = 0;
file_handle.opened_path = NULL;
if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
zend_try {
if (errno == EACCES) {
SG(sapi_headers).http_response_code = 403;
self.mutableResultString = [NSMutableString stringWithString:@"Access denied."];
} else {
SG(sapi_headers).http_response_code = 404;
self.mutableResultString = [NSMutableString stringWithString:@"No input file specified."];
}
} zend_catch {
} zend_end_try();
STR_FREE(SG(request_info).path_translated);
php_request_shutdown((void *)0);
[self shutdownModule];
return NO;
}
php_execute_script(&file_handle TSRMLS_CC);
exit_status = SUCCESS;
}
} zend_catch {
} zend_end_try();
[self shutdownModule];
return (exit_status == SUCCESS);
}
- (NSString *)resultString { return [NSString stringWithString:self.mutableResultString]; }
@end
结果:
2016-11-04 20:05:48.908176 PHPProcessor Test[36988:2522597] verifying buffer from read_post: a=123&b=456
2016-11-04 20:05:48.913119 PHPProcessor Test[36988:2522597] resultString:
<pre>
GET: Array
(
)
POST: Array
(
[a] => 123
[b] => 456
)
REQUEST: Array
(
[a] => 123
[b] => 456
)
COOKIE: Array
(
)
SERVER: Array
(
[SERVER_SOFTWARE] => PHPProcessor Test
[REQUEST_TIME_FLOAT] => 1478304348.9085
[REQUEST_TIME] => 1478304348
[argv] => Array
(
[0] => phpp
)
[argc] => 1
)
ENV: Array
(
[SHELL] => /bin/bash
...
)
</pre>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
...
<title>phpinfo()</title>
...
<body><div class="center">
<table>
<tr class="h"><td>
<a href="http://www.php.net/"><img border="0" src="data:image/png;base64,..." alt="PHP logo" /></a><h1 class="p">PHP Version 5.6.27</h1>
...
</table>
</div></body></html>
我已经在 macOS 10.12.1 Sierra
上将 PHP 5.6.27
编译为静态库(这本身就是一个不小的壮举)。我已将我的应用程序链接到所有必需的库,并且它正在构建而没有错误。使用静态 PHP 库,我能够执行任意 PHP 代码和执行 PHP 脚本。但是,如果我尝试使用表单数据(从同一应用程序中的嵌入式 HTTP 服务器检索)处理脚本,则永远不会填充 $_POST
全局(如果请求,$_GET
全局设置得很好是一个 GET
,在 URL).
不包括整个项目,这里是我的 PHP 处理器 class 和我的自定义 PHP 模块和一个用于测试处理器的存根,没有实际的 HTTP 请求但具有相同的请求生成的内容是我的 class 需要的。您可以在底部看到,在 t运行 的结果中,PHP 脚本是 运行 并且正在调用 read_post 方法并正确加载提供的缓冲区 PHP 然后应该用来填充 $_POST
数组,但 $_POST
数组仍然是空的(尽管已初始化)。任何提示将不胜感激。
PHPProcessor.h:
#import <Cocoa/Cocoa.h>
@interface PHPProcessor : NSObject
@property (strong) NSString *requestMethod;
@property (strong) NSURL *requestURL;
@property (strong) NSString *requestContentType;
@property (strong) NSData *bodyData;
@property (readonly) NSString *resultString;
+ (instancetype)phpProcessor;
- (BOOL)executeScript:(NSString *)filepath;
@end
PHPProcessor.m:
#import "PHPProcessor.h"
#include "php.h"
#include "php_main.h"
#include "php_variables.h"
@interface PHPProcessor ()
@property (assign) NSInteger bodyDataOffset;
@property (strong) NSString *resultString;
@end
static int phpp_startup(sapi_module_struct *sapi_module) {
return ((php_module_startup(sapi_module, NULL, 0) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_shutdown(sapi_module_struct *sapi_module) {
return ((php_module_shutdown_wrapper(sapi_module) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_ub_write(const char *str, unsigned int str_length TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSString *newString = [NSString stringWithUTF8String:str];
if (!newString) newString = [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
if (newString) {
NSString *resultString = phpProcessor.resultString;
if (!resultString) resultString = [NSString string];
resultString = [resultString stringByAppendingString:newString];
phpProcessor.resultString = resultString;
return str_length;
}
return 0;
}
static int phpp_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
static int phpp_read_post(char *buffer, uint count_bytes TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSData *bodyData = phpProcessor.bodyData;
uint remaining_bytes = (uint)(bodyData.length - phpProcessor.bodyDataOffset);
count_bytes = MIN(count_bytes, remaining_bytes);
[bodyData getBytes:buffer range:NSMakeRange(phpProcessor.bodyDataOffset, count_bytes)];
phpProcessor.bodyDataOffset += count_bytes;
#if DEBUG
NSLog(@"verifying buffer from read_post: %@", [[NSString alloc] initWithBytes:buffer length:phpProcessor.bodyDataOffset encoding:NSASCIIStringEncoding]);
#endif
return count_bytes;
}
static void phpp_register_variable(int arg, zval *track_vars_array, const char *key, const char *val TSRMLS_DC) {
char *new_val = (char *)val; uint new_val_len;
if (val && sapi_module.input_filter(arg, (char *)key, &new_val, (unsigned int)strlen(val), &new_val_len TSRMLS_CC)) php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC);
}
static void phpp_register_server_variables(zval *track_vars_array TSRMLS_DC) {
phpp_register_variable(PARSE_SERVER, track_vars_array, "SERVER_SOFTWARE", [[[NSBundle mainBundle] objectForInfoDictionaryKey:(id)kCFBundleNameKey] UTF8String]);
}
static void phpp_log_message(char *message TSRMLS_DC) {
printf("%s\n", message);
}
sapi_module_struct phpp_module = {
"phpp", // char *name;
"PHP Processor Module", // char *pretty_name;
phpp_startup, // int (*startup)(struct _sapi_module_struct *sapi_module);
phpp_shutdown, // int (*shutdown)(struct _sapi_module_struct *sapi_module);
NULL, // int (*activate)(TSRMLS_D);
NULL, // int (*deactivate)(TSRMLS_D);
phpp_ub_write, // int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);
NULL, // void (*flush)(void *server_context);
NULL, // struct stat *(*get_stat)(TSRMLS_D);
NULL, // char *(*getenv)(char *name, size_t name_len TSRMLS_DC);
NULL, // void (*sapi_error)(int type, const char *error_msg, ...);
NULL, // int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
phpp_send_headers, // int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
NULL, // void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);
phpp_read_post, // int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC);
NULL, // char *(*read_cookies)(TSRMLS_D);
phpp_register_server_variables, // void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);
phpp_log_message, // void (*log_message)(char *message TSRMLS_DC);
NULL, // double (*get_request_time)(TSRMLS_D);
NULL, // void (*terminate_process)(TSRMLS_D);
"", // char *php_ini_path_override; // "" causes $_ENV to be set
STANDARD_SAPI_MODULE_PROPERTIES
};
@implementation PHPProcessor
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sapi_module_struct *sapi_module = &phpp_module;
sapi_startup(sapi_module);
sapi_module->startup(sapi_module);
});
}
+ (instancetype)phpProcessor { return [self new]; }
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
- (BOOL)executeScript:(NSString *)filepath {
self.resultString = nil;
self.bodyDataOffset = 0;
SG(request_info).request_method = (char *)self.requestMethod.UTF8String;
SG(request_info).content_type = (char *)self.requestContentType.UTF8String;
SG(request_info).content_length = (long)self.bodyData.length;
SG(request_info).request_uri = (char *)self.requestURL.relativePath.UTF8String;
SG(request_info).path_translated = (char *)filepath.UTF8String;
SG(request_info).query_string = (char *)self.requestURL.query.UTF8String;
if (php_request_startup(TSRMLS_C) == SUCCESS) {
SG(server_context) = (void *)CFBridgingRetain(self);
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = filepath.UTF8String;
file_handle.free_filename = 0;
file_handle.opened_path = NULL;
php_execute_script(&file_handle TSRMLS_CC);
php_request_shutdown(NULL);
SG(server_context) = NULL;
CFRelease(self);
return YES;
}
return NO;
}
@end
test.php:
<?php
echo("<pre>\n");
echo("GET: ");
print_r($_GET);
echo("POST: ");
print_r($_POST);
echo("SERVER: ");
print_r($_SERVER);
echo("ENV: ");
print_r($_ENV);
echo("</pre>\n");
phpinfo();
?>
要使用的代码PHP处理器:
NSString *filepath = @"~/Desktop/test.php".stringByExpandingTildeInPath;
PHPProcessor *phpProcessor = [PHPProcessor phpProcessor];
phpProcessor.requestMethod = @"POST";
phpProcessor.requestURL = [NSURL fileURLWithPath:filepath];
phpProcessor.requestContentType = @"application/x-www-form-urlencoded";
phpProcessor.bodyData = [@"a=123&b=456" dataUsingEncoding:NSUTF8StringEncoding];
[phpProcessor executeScript:filepath];
NSLog(@"resultString:\n\n%@", phpProcessor.resultString);
结果:
2016-10-30 17:26:10.429404 PHPProcessor Test[38190:2886757] verifying buffer from read_post: a=123&b=456
2016-10-30 17:26:10.429931 PHPProcessor Test[38190:2886757] resultString:
<pre>
GET: Array
(
)
POST: Array
(
)
SERVER: Array
(
[SERVER_SOFTWARE] => PHPProcessor Test
[REQUEST_TIME_FLOAT] => 1477862770.425
[REQUEST_TIME] => 1477862770
[argv] => Array
(
)
[argc] => 0
)
ENV: Array
(
[SHELL] => /bin/bash
...
)
</pre>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
...
<title>phpinfo()</title>
...
<body><div class="center">
<table>
<tr class="h"><td>
<a href="http://www.php.net/"><img border="0" src="data:image/png;base64,..." alt="PHP logo" /></a><h1 class="p">PHP Version 5.6.27</h1>
...
</table>
</div></body></html>
对于那些对解决方案感兴趣的人,基本上归结为将 sapi_module_struct
启动移动到 class initialization
方法之外,并从实例方法调用它。我还稍微清理了其余代码,出于某种原因,为了避免崩溃,我不得不添加一个用于读取 cookie 的函数,即使我最初对它们不感兴趣(返回一个有效的 cookie 字符串确实设置了$_COOKIE
数组)。希望这可以帮助其他人。
已修订 PHPProcessor.m:
#import "PHPProcessor.h"
#include "php.h"
#include "php_main.h"
#include "php_variables.h"
@interface PHPProcessor ()
@property (assign) NSInteger bodyDataOffset;
@property (strong) NSMutableString *mutableResultString;
@end
static int phpp_startup(sapi_module_struct *sapi_module) {
return ((php_module_startup(sapi_module, NULL, 0) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_shutdown(sapi_module_struct *sapi_module) {
return ((php_module_shutdown_wrapper(sapi_module) == FAILURE) ? FAILURE : SUCCESS);
}
static int phpp_ub_write(const char *str, unsigned int str_length TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSString *newString = [NSString stringWithUTF8String:str];
if (!newString) newString = [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
if (newString) {
[phpProcessor.mutableResultString appendString:newString];
return str_length;
}
return 0;
}
static int phpp_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
static int phpp_read_post(char *buffer, uint count_bytes TSRMLS_DC) {
PHPProcessor *phpProcessor = (__bridge PHPProcessor *)SG(server_context);
NSData *bodyData = phpProcessor.bodyData;
uint remaining_bytes = (uint)(bodyData.length - phpProcessor.bodyDataOffset);
count_bytes = MIN(count_bytes, remaining_bytes);
[bodyData getBytes:buffer range:NSMakeRange(phpProcessor.bodyDataOffset, count_bytes)];
phpProcessor.bodyDataOffset += count_bytes;
#if DEBUG
NSLog(@"verifying buffer from read_post: %@", [[NSString alloc] initWithBytes:buffer length:phpProcessor.bodyDataOffset encoding:NSASCIIStringEncoding]);
#endif
return count_bytes;
}
static char *phpp_read_cookies(TSRMLS_D) {
return NULL;
}
static void phpp_register_variable(int arg, zval *track_vars_array, const char *key, const char *val TSRMLS_DC) {
char *new_val = (char *)val; uint new_val_len;
if (val && sapi_module.input_filter(arg, (char *)key, &new_val, (unsigned int)strlen(val), &new_val_len TSRMLS_CC)) php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC);
}
static void phpp_register_server_variables(zval *track_vars_array TSRMLS_DC) {
phpp_register_variable(PARSE_SERVER, track_vars_array, "SERVER_SOFTWARE", [[[NSBundle mainBundle] objectForInfoDictionaryKey:(id)kCFBundleNameKey] UTF8String]);
}
static void phpp_log_message(char *message TSRMLS_DC) {
printf("%s\n", message);
}
sapi_module_struct phpp_module = {
"phpp", // char *name;
"PHP Processor Module", // char *pretty_name;
phpp_startup, // int (*startup)(struct _sapi_module_struct *sapi_module);
phpp_shutdown, // int (*shutdown)(struct _sapi_module_struct *sapi_module);
NULL, // int (*activate)(TSRMLS_D);
NULL, // int (*deactivate)(TSRMLS_D);
phpp_ub_write, // int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);
NULL, // void (*flush)(void *server_context);
NULL, // struct stat *(*get_stat)(TSRMLS_D);
NULL, // char *(*getenv)(char *name, size_t name_len TSRMLS_DC);
php_error, // void (*sapi_error)(int type, const char *error_msg, ...);
NULL, // int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
phpp_send_headers, // int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
NULL, // void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);
phpp_read_post, // int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC);
phpp_read_cookies, // char *(*read_cookies)(TSRMLS_D);
phpp_register_server_variables, // void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);
phpp_log_message, // void (*log_message)(char *message TSRMLS_DC);
NULL, // double (*get_request_time)(TSRMLS_D);
NULL, // void (*terminate_process)(TSRMLS_D);
"", // char *php_ini_path_override; // "" causes $_ENV to be set
STANDARD_SAPI_MODULE_PROPERTIES
};
@implementation PHPProcessor
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sapi_startup(&phpp_module);
});
}
+ (instancetype)phpProcessor { return [self new]; }
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
- (void)shutdownModule {
SG(server_context) = NULL;
CFRelease(self);
php_module_shutdown(TSRMLS_C);
}
- (BOOL)executeScript:(NSString *)filepath {
self.bodyDataOffset = 0;
self.mutableResultString = [NSMutableString string];
if (phpp_module.startup(&phpp_module) == FAILURE) return NO;
int exit_status = FAILURE;
zend_first_try {
SG(server_context) = (void *)CFBridgingRetain(self);
SG(request_info).request_method = (char *)self.requestMethod.UTF8String;
SG(request_info).content_type = (char *)self.requestContentType.UTF8String;
SG(request_info).content_length = (long)self.bodyData.length;
SG(request_info).request_uri = (char *)self.requestURL.relativePath.UTF8String;
SG(request_info).path_translated = (char *)filepath.fileSystemRepresentation;
SG(request_info).query_string = (char *)self.requestURL.query.UTF8String;
char *argv[2] = {phpp_module.name, NULL};
SG(request_info).argc = 1;
SG(request_info).argv = argv;
SG(request_info).argv0 = phpp_module.name;
SG(sapi_headers).http_response_code = 200;
if (php_request_startup(TSRMLS_C) == FAILURE) {
[self shutdownModule];
return NO;
}
if (SG(request_info).path_translated) {
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = SG(request_info).path_translated;
file_handle.free_filename = 0;
file_handle.opened_path = NULL;
if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
zend_try {
if (errno == EACCES) {
SG(sapi_headers).http_response_code = 403;
self.mutableResultString = [NSMutableString stringWithString:@"Access denied."];
} else {
SG(sapi_headers).http_response_code = 404;
self.mutableResultString = [NSMutableString stringWithString:@"No input file specified."];
}
} zend_catch {
} zend_end_try();
STR_FREE(SG(request_info).path_translated);
php_request_shutdown((void *)0);
[self shutdownModule];
return NO;
}
php_execute_script(&file_handle TSRMLS_CC);
exit_status = SUCCESS;
}
} zend_catch {
} zend_end_try();
[self shutdownModule];
return (exit_status == SUCCESS);
}
- (NSString *)resultString { return [NSString stringWithString:self.mutableResultString]; }
@end
结果:
2016-11-04 20:05:48.908176 PHPProcessor Test[36988:2522597] verifying buffer from read_post: a=123&b=456
2016-11-04 20:05:48.913119 PHPProcessor Test[36988:2522597] resultString:
<pre>
GET: Array
(
)
POST: Array
(
[a] => 123
[b] => 456
)
REQUEST: Array
(
[a] => 123
[b] => 456
)
COOKIE: Array
(
)
SERVER: Array
(
[SERVER_SOFTWARE] => PHPProcessor Test
[REQUEST_TIME_FLOAT] => 1478304348.9085
[REQUEST_TIME] => 1478304348
[argv] => Array
(
[0] => phpp
)
[argc] => 1
)
ENV: Array
(
[SHELL] => /bin/bash
...
)
</pre>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
...
<title>phpinfo()</title>
...
<body><div class="center">
<table>
<tr class="h"><td>
<a href="http://www.php.net/"><img border="0" src="data:image/png;base64,..." alt="PHP logo" /></a><h1 class="p">PHP Version 5.6.27</h1>
...
</table>
</div></body></html>