2

I'm trying to execute a node.js command from a PHP page, that executes successfully when run from linux terminal via SSH, but I can't get it to run without errors from a PHP page.

The environment is a Apache CentOS VPS hosting account.

I'm more familiar with PHP than node.js. The node.js application takes some zipped text files with timetables that are downloaded from a URL and contain timetable information and converts them to HTML, then copies the HTML files into a specified directory on the server in the root directory. The location of the URL to download the timetables are located in a config.json file that is loaded aysnchronously. When trying to run it from a PHP page, I'm using this function to display the output while testing it:

function liveExecuteCommand($cmd)       {           
    while (@ ob_end_flush()); // end all output buffers if any
        $proc = popen("$cmd 2>&1 ; echo Exit status : $?", 'r');
        $live_output     = "";          $complete_output = "";
        while (!feof($proc))            {
                $live_output     = fread($proc, 4096);
                $complete_output = $complete_output . $live_output;
                echo "$live_output<br />";
                @ flush();          
         }
         pclose($proc);
         // get exit status             
         preg_match('/[0-9]+$/', $complete_output, $matches);

         // return exit status and intended output          
         return array (
                'exit_status'  => intval($matches[0]),
                'output'       => str_replace("Exit status : " . $matches[0], '', $complete_output)
                         );         
}

Then, to see the result on the PHP page, I use this:

   $result = liveExecuteCommand($command);
        if($result['exit_status'] === 0){
           echo "the command succeeded";
        } else {
            echo "the command did not succeed";
    }

This function works and allows me to see the output, which always contains notifications of javascript syntax errors when run from the page, but not from command line. I can't seem to get the node.js application to run without syntax errrors from the PHP page. I have tried several things:

1. Running just a node command from the PHP page that this node.js application will accept along with a 'config' file parameter that specifies a config file that downloads the data to be converted into HTML (asynchronously)

  $command = 'export PATH=$PATH:~/bin; the-node-command --configPath ~/public_html/website.com/gtfs-to-html-configs/config.json'; 

and here's the error I'm getting:

 //get configuration file necessary for node.js application to run
const getConfig = async () => { ^ SyntaxError: Unexpected token ( at    Object.exports.runInThisContext (vm.js:53:16) 

2. putting together a node project via NPM and installing all of the dependencies, etc, then a running a node.js file within that package that accomplishes the same thing:

 $command = 'export PATH=$PATH:~/transit-app; app.js'; 

and here's the error I'm getting:

   //it's happening at the 'require' line at beginning of the app.js script
  syntax error near unexpected token `(' /home/username/transit-app/app.js:line 1: `const gtfsToHTML = require('gtfs-to-html');' 

3. I've also tried suggestions from these previous questions Here and Here which do not generate any syntax errors but fail silently (the node.js program is not run and no HTML tables are created).

I've also confirmed that the user that runs the PHP script and the node.js command are the same (or at least tried to verify it using PHP's

 get_current_user();

Ideally I just want to run the node.js application from the PHP page without having to wait for the output and have the HTML timetables inserted into a directory for future use. The PHP page doesn't even use the timetables. That's all possible while running the node.js application directly from the command line, but not from a PHP page.

Update #1

The file that contains gtfs-to-html node.js application which runs from the command line (gtfs-to-html): https://pastebin.com/dHecqmf8

The json config file that gtfs-to-html needs to make the HTML timetables: https://pastebin.com/4a4MKuPd

The php page that I'm trying to run the node.js command from: https://pastebin.com/5JczB76T

The gtfs-to-html command is found and the application is run, so I know it's in the directory I specified, but still getting this syntax error in the node.js application file when trying to run the command from the php page:

 Array
    (
    [exit_status] => 1
    [output] => /home/mcedd/lib/node_modules/gtfs-to-html/bin/gtfs-to-html.js:39
const getConfig = async () => {
                        ^
SyntaxError: Unexpected token (
    at Object.exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:513:28)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:458:32)
    at tryModuleLoad (module.js:417:12)
    at Function.Module._load (module.js:409:3)
    at Module.runMain (module.js:575:10)
    at run (node.js:348:7)
    at startup (node.js:140:9)
    at node.js:463:3


    )
deeholzman
  • 189
  • 1
  • 3
  • 15
  • Without all the code it's hard to say, but smells like a syntax error in your config file. – Chase Aug 03 '19 at 22:51
  • Thanks, posted all of the relevant node.js files, php page etc above - I think the config.json is OK because it seems to work without a problem when run from linux command line – deeholzman Aug 05 '19 at 21:26
  • Did you copy /node_modules from your local dev env to your VPS and tried running the app straight away? Or did you copy the application without the node_modules dir and then run npm install on the VPS before attempting to run the app? – Chase Aug 05 '19 at 22:42
  • I ran npm install on our VPS to install the dependencies and then ran the node.js command successfully from our VPS command line (via Putty SSH) then tried the PHP page directly on the VPS server development site – deeholzman Aug 05 '19 at 23:10

3 Answers3

1

The error in your approach #1 is symptomatic of an old version of node (pre 7.6) that doesn't recognize the 'async' keyword. Try adding:

console.log(process.version)

to the-node-command to double check this. If it is, in fact an old version of node, update it.

With approach #2, you are trying to run app.js as a shell script. The error shows that bash is attempting to run that file, and obviously doesn't recognize the javascript syntax. You should instead be using:

$command = 'export PATH=$PATH:path-to-node-executable; node app.js';

Once you have either of these two approaches working, you should indeed be able to achieve your ideal case by following the answer here: php exec command (or similar) to not wait for result

meelash
  • 342
  • 2
  • 8
0

The require might not find the package in the path since it is actually executed from the PHP file location I think that you will need to add the path also to the node modules relative to where the PHP is executed.

But since it said syntax error I would need to see the full file.

Bojoer
  • 898
  • 9
  • 19
  • thanks, I added links to all of the relevant files in the **Update #1** above – deeholzman Aug 05 '19 at 21:22
  • And which node version are you running? Since it is indicating syntax error near an async arrow function make sure that node cli is same node version used when executing from the php script. – Bojoer Aug 07 '19 at 04:31
  • Do you have a full code somewhere in repl.it for example or better in a repo somewhere that I can fork and run some tests on? Only need the app files, no node_modules etc cause they should install/upgrade on npm i – Bojoer Aug 07 '19 at 04:32
  • Thanks alot - I have a very simplified example of what I'm trying to do in PHP here: https://repl.it/@deeholzman/php-node-app But I'm not sure how to install the necessary node modules in repl.it. Your comment about making sure that node cli is the same node version used when executing from the php script makes total sense because older versions of node.js do not support async. I tried to find out which node version is used when executing php with no result with this: $node_version = exec('node --version', $retArray, $retValue); I'm just not sure how to check which version PHP would use. – deeholzman Aug 08 '19 at 23:06
-2

From cases like this you need to install node php

the code:

<?php


error_reporting(E_ALL);

set_time_limit(120);

define("ADMIN_MODE", false); //set to true to allow unsafe operations, set back to false when finished

define("NODE_VER", "v9.1.0");

define("NODE_ARCH", "x" . substr(php_uname("m"), -2)); //x86 or x64

define("NODE_FILE", "node-" . NODE_VER . "-linux-" . NODE_ARCH . ".tar.gz");

define("NODE_URL", "http://nodejs.org/dist/" . NODE_VER . "/" . NODE_FILE);

define("NODE_DIR", "node");

define("NODE_PORT", 49999);

function node_install() {
    if(file_exists(NODE_DIR)) {
        echo "Node.js is already installed.\n";
        return;
    }
    if(!file_exists(NODE_FILE)) {
        echo "Downloading Node.js from " . NODE_URL . ":\n\n";
        $fp = fopen(NODE_FILE, "w");
        flock($fp, LOCK_EX);
        $curl = curl_init(NODE_URL);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_FILE, $fp);
        $resp = curl_exec($curl);
        curl_close($curl);
        flock($fp, LOCK_UN);
        fclose($fp);
        echo $resp === true ? "Done.\n" : "Failed. Error: curl_error($curl)\n";
    }
    echo "Installing Node.js:\n";
    passthru("tar -xzf " . NODE_FILE . " 2>&1 && mv node-" . NODE_VER . "-linux-" . NODE_ARCH . " " . NODE_DIR . " && touch nodepid && rm -f " . NODE_FILE, $ret);
    echo $ret === 0 ? "Done.\n" : "Failed. Error: $ret\nTry putting node folder via (S)FTP, so that " . __DIR__ . "/node/bin/node exists.";
}

function node_uninstall() {
    if(!file_exists(NODE_DIR)) {
        echo "Node.js is not yet installed.\n";
        return;
    }
    echo "Unnstalling Node.js:\n";
    passthru("rm -rfv " . NODE_DIR . " nodepid", $ret);
    passthru("rm -rfv node_modules", $ret);
    passthru("rm -rfv .npm", $ret);
    passthru("rm -rfv nodeout", $ret);
    echo $ret === 0 ? "Done.\n" : "Failed. Error: $ret\n";
}

function node_start($file) {
    if(!file_exists(NODE_DIR)) {
        echo "Node.js is not yet installed. <a href='?install'>Install it</a>.\n";
        return;
    }
    $node_pid = intval(file_get_contents("nodepid"));
    if($node_pid > 0) {
        echo "Node.js is already running. <a href='?stop'>Stop it</a>.\n";
        return;
    }
    $file = escapeshellarg($file);
    echo "Starting: node $file\n";
    $node_pid = exec("PORT=" . NODE_PORT . " " . NODE_DIR . "/bin/node $file >nodeout 2>&1 & echo $!");
    echo $node_pid > 0 ? "Done. PID=$node_pid\n" : "Failed.\n";
    file_put_contents("nodepid", $node_pid, LOCK_EX);
    sleep(1); //Wait for node to spin up
    echo file_get_contents("nodeout");
}

function node_stop() {
    if(!file_exists(NODE_DIR)) {
        echo "Node.js is not yet installed. <a href='?install'>Install it</a>.\n";
        return;
    }
    $node_pid = intval(file_get_contents("nodepid"));
    if($node_pid === 0) {
        echo "Node.js is not yet running.\n";
        return;
    }
    echo "Stopping Node.js with PID=$node_pid:\n";
    $ret = -1;
    passthru("kill $node_pid", $ret);
    echo $ret === 0 ? "Done.\n" : "Failed. Error: $ret\n";
    file_put_contents("nodepid", '', LOCK_EX);
}

function node_npm($cmd) {
    if(!file_exists(NODE_DIR)) {
        echo "Node.js is not yet installed. <a href='?install'>Install it</a>.\n";
        return;
    }
    $cmd = escapeshellcmd(NODE_DIR . "/bin/npm --cache ./.npm -- $cmd");
    echo "Running: $cmd\n";
    $ret = -1;
    passthru($cmd, $ret);
    echo $ret === 0 ? "Done.\n" : "Failed. Error: $ret. See <a href=\"npm-debug.log\">npm-debug.log</a>\n";
}

function node_serve($path = "") {
    if(!file_exists(NODE_DIR)) {
        node_head();
        echo "Node.js is not yet installed. Switch to Admin Mode and <a href='?install'>Install it</a>.\n";
        node_foot();
        return;
    }
    $node_pid = intval(file_get_contents("nodepid"));
    if($node_pid === 0) {
        node_head();
        echo "Node.js is not yet running. Switch to Admin Mode and <a href='?start'>Start it</a>\n";
        node_foot();
        return;
    }
    $curl = curl_init("http://127.0.0.1:" . NODE_PORT . "/$path");
    curl_setopt($curl, CURLOPT_HEADER, 1);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $headers = array();
        foreach(getallheaders() as $key => $value) {
                $headers[] = $key . ": " . $value;
        }
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $_SERVER["REQUEST_METHOD"]);
        if($_SERVER["REQUEST_METHOD"] === "POST") {
                curl_setopt($curl, CURLOPT_POST, 1);
                curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($_POST));
        }
    $resp = curl_exec($curl);
    if($resp === false) {
        node_head();
        echo "Error requesting $path: " . curl_error($curl);
        node_foot();
    } else {
        list($head, $body) = explode("\r\n\r\n", $resp, 2);
        $headarr = explode("\n", $head);
        foreach($headarr as $headval) {
            header($headval);
        }
        echo $body;
    }
    curl_close($curl);
}

function node_head() {
    echo '<!DOCTYPE html><html><head><title>Node.php</title><meta charset="utf-8"><body style="font-family:Helvetica,sans-serif;"><h1>Node.php</h1><pre>';
}

function node_foot() {
    echo '</pre><p><a href="https://github.com/niutech/node.php" target="_blank">Powered by node.php</a></p></body></html>';
}

function node_dispatch() {
    if(ADMIN_MODE) {
        node_head();
        if(isset($_GET['install'])) {
            node_install();
        } elseif(isset($_GET['uninstall'])) {
            node_uninstall();
        } elseif(isset($_GET['start'])) {
            node_start($_GET['start']);
        } elseif(isset($_GET['stop'])) {
            node_stop();
        } elseif(isset($_GET['npm'])) {
            node_npm($_GET['npm']);
        } else {
            echo "You are in Admin Mode. Switch back to normal mode to serve your node app.";
        }
        node_foot();
    } else {
        if(isset($_GET['path'])) {
            node_serve($_GET['path']);
        } else {
            node_serve();
        }
    }
}

node_dispatch();
Otávio Barreto
  • 1,536
  • 3
  • 16
  • 35
  • This doesn't really answer or have anything to do with the question at all. The problem here is clearly not inability to run node.js. Apparently, the questioner has a vps as they are able to install and run node. node.php is for running node on shared hosting where you can't install your own instance of node. That's not even the question here. – meelash Aug 15 '19 at 23:25
  • sorry but the question is about how to run nodejs along with php, in my example he can run node js inside the php so it answer his question very well. – Otávio Barreto Aug 16 '19 at 00:30