2015-08-16 35 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()。用這兩個語句包裝我的主代碼使所有事情都能夠正常工作。希望這將會拯救一些人頭痛的道路:)