12
回答
C/C++调用PHP
终于搞明白,存储TCO原来是这样算的>>>   

由于缺少C/C++程序员,所以想用C/C++做控制器调用PHP(PHP的人不缺),主要由PHP来写逻辑,由C来调用,但不能用HTTP的方式去调用PHP,最好可以像“$system=new Java("java.lang.System");print "Java version=
".$system->getProperty("java.version")." \n";”PHP调JAVA的方式,如果有更好然记前辈们推荐一下,或着直接高诉我,这只是个梦。

 

PHP这边全部以OOP方式去写,然后C那边可以NEW 一个类,然后调用一个方法,并传参。

前辈们看看怎么解决。

举报
陆钧
发帖于7年前 12回/7K+阅
共有12个答案 最后回答: 4年前

  • #include "sapi/embed/php_embed.h"
  •  
  • int main(int argc, char * argv[]){
  •     PHP_EMBED_START_BLOCK(argc,argv);
  •     char * script = " print 'Hello World!';";
  •     zend_eval_string(script, NULL,
  •                                       "Simple Hello World App" TSRMLS_CC);
  •     PHP_EMBED_END_BLOCK();
  •     return 0;
  • }

Embedding the PHP Interpreter

Latviskajai versijai spied šeit

Few weeks ago I had to use iDealer PHP library from C++. I did that and now the code has been tested and is working fine.

I had embedded Python before, so I knew what I had to do: initialize the PHP interpreter, create some variables, execute PHP code, read some PHP variables and shutdown the PHP interpreter. The first and the last step will be executed only once for whole server lifetime (performance considerations) while steps 2-4 will be executed for every request. To keep it simple, I will use global PHP variables and global symbol table known as EG(symbol_table) in C code and $GLOBALS in PHP code. To keep it even more simple, I will use only string PHP variables and will create a correct type (array for example) in PHP code.

I can’t assure that everything written below is 100% correct, since PHP documentation was really poor, especially compared to Python. I found a part of solutions in Zend source code, so there might be a better and cleaner solutions. I also spent some time using gdb (The GNU Debugger) and valgrind and chasing bugs.

Preparation

First you need to install PHP. I compiled version 5.0.4 from sources:

env CC=gcc ./configure --enable-embed --prefix=/home/aivarsk/php && make && make install

The most important here is --enable-embed flag that will compile PHP library (libphp5.so) because it’s not compiled by default. At the same time it compiles sapi/embed module, that in theory should let you execute PHP in C, but in practice it sounds a bit different. Anyway, you may look at that code for some ideas.

1. Initialization of the PHP interpreter

That’s not so easy, so you should use php_embed_init() from sapi/embed module. I’m using some piece of it as I don’t understand everything it does.

static char *argv[2] = {"myname", NULL};
if (php_embed_init(1, argv PTSRMLS_CC) == FAILURE) {
    /* Uhhh? */
}

You should implement following 3 functions that are used for output. I, for example, forwarded all output to logfiles. Following lines are ripped from Irssi presentation:

static int ub_write(const char *str, unsigned int str_length TSRMLS_DC)
{
/* php-irssi line-buffers output and then renders line-by-line
 * to one of the console windows. */
}
static void log_message(char *message)
{
/* catch default output for log_errors; these are the messages
 * that end up in your apache error log for example */
}
static void sapi_error(int type, const char *fmt, ...)
{
/* Catch some low-level SAPI errors */
}

There is a global structure php_embed_module where you should override default PHP functions with your own.

php_embed_module.ub_write = ub_write;
php_embed_module.log_message = log_message;
php_embed_module.sapi_error = sapi_error;

2. Creating variables

PHP documentation suggests using SET_VAR_STRING, but I found out a problem: it does not create a copy of value and PHP code unset($var) or PHP shutdown with php_request_shutdown() leads to Segmentation fault. I replaced this macro with following code:

zval  *var;
ALLOC_ZVAL(var); */
/* ZVAL_STRING(var, value, 0); is wrong*/
ZVAL_STRING(var, value, 1);
ZEND_SET_GLOBAL_VAR(name, var);

Zend documentation says that instead of ZEND_SET_GLOBAL_VAR() you can use a more optimal code, but you will have to use also MAKE_STD_ZVAL() to update reference counter.

zval  *var;
MAKE_STD_ZVAL(var); */
ZVAL_STRING(var, value, 1);
(void)zend_hash_update(&EG(symbol_table), name, strlen(name) + 1, &var, sizeof(zval *), NULL);

3. Execution of PHP code

You can find some information in Zend documentation, but that was not useful for me. Google found some information about zend_eval_string() and I’m using it:

zend_first_try {
    if (zend_eval_string("echo 'foobar';", NULL, __func__) == FAILURE) {
        /* Syntax error */
    }
} zend_catch {
} zend_end_try();

Current function name __func__ is passed for nicer stack trace in case of error.
The main problem here is that zend_eval_string() returns an error only because of invalid PHP code syntax and returns success even when an exception occurs. Even more, unhandled exceptions will cause next zend_eval_string() call end with Segmentation fault. You can solve that by wrapping PHP code with try/catch and assigning result code to a global variable (done in this case):

$done = false;
try {
    call_some_function($arg); /* Throws exception on error */
    $done = true;
} catch (Exception $e) {
    /* Error handling */
}

4. Reading variables

Zend documentation is silent about this. Still, after experience with creation of variables it is obvious that you can find them in global symbol table. Since symbol table is just a Zend HashTable after short grep-ing I found zend_hash_find(). Somewhere before I’ve seen convert_to_string() that converts any type to string.

zval **data = NULL;
if (zend_hash_find(&EG(symbol_table), name, strlen(name) + 1, (void **)&data) == FAILURE) {
    /* Name not found in $GLOBALS */
}
if (data == NULL) {
    /* Value is NULL (not possible for symbol_table?) */
}
convert_to_string(*data); /* We need string value */
std::string value(Z_STRVAL(**data), Z_STRLEN(**data));

Since I’m using the same environment for executing every request, I have to cleanup unused variables. I’m sure that I could find some zend_hash_ function that does that. But I’m lazy, so I just call zend_eval_string() again and delete global variables in PHP code:

unset($foo);
unset($bar);

5. Shutdown of the PHP interpreter

It’s safe to use php_embed_shutdown() function from sapi/embed:

php_embed_shutdown(TSRMLS_C);

That’s all! Oh, and pardon my english.

Reading

引用来自#5楼“zeussam”的帖子

重要逻辑用PHP写,而一个控制器用C写有什么意义呢?

 一是一个技术实现,二是人员问题

引用来自#7楼“苏伟”的帖子

直接发送请求到PHP-CGI运行PHP

php.exe会产生服务器进程。 如果同时交行调用太多,且计算超过1秒以上或更长时间,这样进程会过多。

为什么用c 调用php,其实php是一个c/c++的桥语言,基本上就是c/c++一个变种。可以直接做到php里面

apc可以将php编译城php 的 bytecode 导出到文件的,实现代码保护

引用来自#10楼“宏哥”的帖子

为什么用c 调用php,其实php是一个c/c++的桥语言,基本上就是c/c++一个变种。可以直接做到php里面

apc可以将php编译城php 的 bytecode 导出到文件的,实现代码保护

CLI的程序可以用bcompiler来编译

顶部