2015-08-16 39 views
2

我想写一个c程序,可以分析PHP脚本出于各种原因。我需要从PHP核心库中调用token_get_all,但我正在为此苦苦挣扎。我已经静态编译php7主分支,并将其链接到我的程序。我会发布几个代码片段,因为我已经尝试了很多方法来调用这个函数,并且一直运行在一种或另一种类型的错误中。从c程序调用token_get_all

这个片断得到,当我执行它最远:

int main(void) 
{ 
    zval function_name, params[1], return_value, param; 
    ZVAL_NEW_STR(&function_name, zend_string_init("token_get_all", strlen("token_get_all"), 1)); 

    printf("Got here\n"); 

    ZVAL_NEW_STR(&params[0], zend_string_init("<?php $x = 1;", strlen("<?php $x = 1;"), 1)); 
    int ret; 

    printf("Calling function\n"); 

    ret = call_user_function(CG(function_table), NULL, &function_name, &return_value, 1, params TSRMLS_CC); 
    printf("%i", ret); 
} 

当它到达call_user_function,它出现segfaults。 Valgrind输出:

==11451== 1 errors in context 1 of 1: 
==11451== Invalid read of size 8 
==11451== at 0x40268C: parse_php (parser.c:73) 
==11451== by 0x402730: parse_php_file (parser.c:95) 
==11451== by 0x4029B2: main (parse-script-main.c:14) 
==11451== Address 0x0 is not stack'd, malloc'd or (recently) free'd 

parser.c:73是call_user_function的行。

我会发布这个以及,虽然它可能是一个单独的问题。如果我改变了我初始化函数名或独立参数的方式,那么我会用不同的段错误结束。试想一下:

int main(void) 
{ 
    zval function_name, params[1], return_value, param; 
    ZVAL_STRING(&function_name, "token_get_all"); 

    printf("Got here\n"); 

    ZVAL_NEW_STR(&params[0], zend_string_init("<?php $x = 1;", strlen("<?php $x = 1;"), 1)); 
    int ret; 

    printf("Calling function\n"); 

    ret = call_user_function(CG(function_table), NULL, &function_name, &return_value, 1, params TSRMLS_CC); 
    printf("%i", ret); 
} 

这给了我一个段错误的ZVAL_STRING行:

==11481== 1 errors in context 1 of 1: 
==11481== Invalid read of size 8 
==11481== at 0x407DF5: _emalloc (zend_alloc.c:2376) 
==11481== by 0x402559: zend_string_alloc (zend_string.h:121) 
==11481== by 0x4025C2: zend_string_init (zend_string.h:157) 
==11481== by 0x402639: parse_php (parser.c:65) 
==11481== by 0x402747: parse_php_file (parser.c:95) 
==11481== by 0x4029C9: main (parse-script-main.c:14) 
==11481== Address 0x0 is not stack'd, malloc'd or (recently) free'd 

最后,这里是我的编译器/连接命令:

gcc -g -D_GNU_SOURCE -Iinclude -I/usr/local/include/php -I/usr/local/include/php/Zend -I/usr/local/include/php/include -I/usr/local/include/php/main -I/usr/local/include/php/ext -I/usr/local/include/php/sapi -I/usr/local/include/php/TSRM -c /path/to/parser.c -o obj/Debug/include/parser.o 
g++ -Linclude -L/usr/lib/x86_64-linux-gnu -L/usr/local/lib -o bin/Debug/parse-script obj/Debug/include/parser.o obj/Debug/include/project.o obj/Debug/include/utils.o obj/Debug/parse-script-main.o -lphp7 -ldl -lc -lpthread -lgcc 

我知道生成的目标文件唐不符合上面的代码。在上面的例子中,我将问题函数封装在“int main(void)”中。

回答

1

好的,我设法克服了这个问题。我会在这里发表我的发现。基本上,当你搜索这个主题时(至少对我而言),你发现的大部分信息都与编写PHP扩展有关,而不是将PHP链接到你的c应用并调用它的一些内部函数。这里是正在为我工​​作:

的main.c:

#include <stdio.h> 
#include <stdlib.h> 
#include <sapi/embed/php_embed.h> 
#include "parser.h" 

int main(int, char *[]); 

int main(int argc, char *argv[]) 
{ 

    if (argc != 2) { 
     fprintf(stdout, "USAGE: parse-script <php-file>\n"); 
     exit(0); 
    } 

    PHP_EMBED_START_BLOCK(argc, argv); 
    parse_php_file(argv[1]); 
    PHP_EMBED_END_BLOCK(); 

    return 0; 
} 

parser.c:

#include <stdio.h> 
#include "utils.h" 
#include "php.h" 

int parse_php(char *code) 
{ 

    zval function_name; 
    zval return_value; 
    int param_count = 1; 
    zval code_param; 
    zval *params[1]; 

    ZVAL_STRINGL(params[0], code, strlen(code), 0); 
    INIT_ZVAL(function_name); 
    ZVAL_STRING(&function_name, "token_get_all", 0); 

    TSRMLS_FETCH(); 

    if (call_user_function(CG(function_table), (zval **)NULL, &function_name, &return_value, 1, params TSRMLS_CC) == SUCCESS) { 
     zend_print_zval_r(&return_value, 0); 
    } else { 
     fprintf(stderr, "Error parsing PHP code.\n"); 
    } 

    printf("Done\n"); 

} 

int parse_php_file(char *file_name) 
{ 
    char *code; 

    code = read_file(file_name); 
    if (code == NULL) { 
     fprintf(stderr, "Could not read file: %s\n", file_name); 
     return 0; 
    } 
    parse_php(code); 
} 

的关键似乎是PHP_EMBED_START_BLOCK()和PHP_EMBED_END_BLOCK()。用这两个语句包装我的主代码使所有事情都能够正常工作。希望这将会拯救一些人头痛的道路:)