The basic idea came from the php-embed SAPI module. I just need a simple customization, but it has too heavy depedencies than I want to do.

To create a code generator similar to Unreal Engine 4. There are so many basic functions that PHP itself has that I think is the best way to make such a tool.

Modules are light and have a little dependencies, but they are static linkable. In addition, the template processing for strings is really powerful, so I want to make it with PHP language.

like this Visual Studio Solution generator:

  Perhaps I'll write PHP code here to identify each C / C ++ module. 
  And, I'll set necessary PHP variables.

  Like, $project_name, $project_path, $included_modules something...


$project_uuid = UUID::generate_from_string_hash($project_name);

?>Microsoft Visual Studio Solution File, Format Version <?=$target_vs_fmt_version?>
# Visual Studio <?=$target_vs_version?>
VisualStudioVersion = <?=$recommended_vs_version?>
MinimumVisualStudioVersion = <?=$minimal_vs_version?>
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "<?=$project_name?>", "<?=$project_name?>.vcxproj", "<?=$project_uuid?>"
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|x64 = Debug|x64
        Debug|x86 = Debug|x86
        Release|x64 = Release|x64
        Release|x86 = Release|x86
    GlobalSection(ProjectConfigurationPlatforms) = postSolution
        <?=$project_uuid?>.Debug|x64.ActiveCfg = Debug|x64
        <?=$project_uuid?>.Debug|x64.Build.0 = Debug|x64
        ... blah blah...
        <?php foreach($included_modules as $module_info) { ?>
            <?=$module_info['uuid']?>.Debug|x64.ActiveCfg = Debug|x64
        <?php } ?>


But an important problem is,

php-cli or php-cgi may read the global php.ini configuration file installed on system. And allowing shell_exec and exec functions to run compilers can cause security problems.

So, the critical problem is,

I should modify those configuration files for running my own build tool.

Of course, it can be solved by giving execution options, which makes the command line too long.

But that's too tired.

php shebang is available, but that's not the method available on Windows systems. (Not impossible, but the process is quite complicated in anyway, rather than having a single executable.)

So, I've wrote a simple SAPI library that is for creating another PHP interpreter based on PHP interpreter. to be precise, it's a PHP SAPI module, but the result is a static library. and it can be linked without phpize and other shared library.

(Of course, dynamic libraries are loaded at runtime. including php7ts.{dll,so}.)


#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>

#include <php_xclie.h>
#pragma comment(lib, "php7xclie.lib")

void bb_preinit(struct xclie_exec*, const char*);
void bb_cleanup(struct xclie_exec*);

#define BB_ENTRY_SCRIPT     "engine/main.php"

int main(int argc, char** argv) {
    struct xclie_exec exec = { 0, };
    bool waitUser = true;
    int retVal = 0;

    exec.argc = argc;
    exec.argv = argv;

    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-q")) {
            waitUser = false;

    /* entry script will be set on bb_preinit. */
    exec.preinit = bb_preinit;
    exec.entry_script = nullptr;

    if ((retVal = xclie_run(&exec)) != XCLIX_SUCCESS) {
        fprintf(stderr, "Jay's Build Tool.\n");
        fprintf(stderr, "based on PHP 7. lol!\n");
        fprintf(stderr, "Copyright (C) 2020 Jaehoon Joe.\n\n");

        switch (retVal) {
        case XCLIX_CANT_STARTUP:
            fprintf(stderr, "Unknown error: PHP startup routines failure.\n");

        case XCLIX_NO_FILE_SPEC:
            fprintf(stderr, "Error: entry script missing!\n >> %s.\n", exec.entry_script);

            fprintf(stderr, "No permission to execute build-script!\n >> %s.\n", exec.entry_script);

        case XCLIX_NO_SUCH_FILE:
            fprintf(stderr, "No such file existed!\n >> %s.\n", exec.entry_script);

        if (waitUser) {
        return -1;

    if (waitUser) {
    return 0;

void bb_preinit(struct xclie_exec* exec, const char* exec_path) {
    int basepath_size   = int(strlen(exec_path));
    int entry_size      = basepath_size + sizeof(BB_ENTRY_SCRIPT) + 1;

    if (char* entry_script = (char*)::malloc(entry_size)) {
        exec->entry_script = entry_script;

        ::memcpy(entry_script, exec_path, basepath_size);
        ::memcpy(entry_script + basepath_size + 1,
            BB_ENTRY_SCRIPT, sizeof(BB_ENTRY_SCRIPT));

        *(entry_script + basepath_size) = '/';

void bb_cleanup(struct xclie_exec* exec) {
    if (exec->entry_script) {

    exec->prepend_script = 
        exec->entry_script = nullptr;

I've rewritten to run PHP in another thread and do something else in C/C ++ code. I also implemented some event mechanism (i think that is something strange although i did.) for communicating between C/C++ and PHP code.

What should I do I create something more in order to further improve this idea?


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.