{"id":18645453,"url":"https://github.com/cscott/node-php-embed","last_synced_at":"2025-04-11T12:31:23.674Z","repository":{"id":57323246,"uuid":"43915468","full_name":"cscott/node-php-embed","owner":"cscott","description":"Bidirectional interoperability between PHP and JavaScript code within the Node.js process.","archived":false,"fork":false,"pushed_at":"2015-11-05T19:38:41.000Z","size":60328,"stargazers_count":36,"open_issues_count":3,"forks_count":5,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-25T13:51:07.601Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cscott.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-08T20:48:11.000Z","updated_at":"2024-11-25T07:14:25.000Z","dependencies_parsed_at":"2022-09-12T06:30:21.444Z","dependency_job_id":null,"html_url":"https://github.com/cscott/node-php-embed","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cscott%2Fnode-php-embed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cscott%2Fnode-php-embed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cscott%2Fnode-php-embed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cscott%2Fnode-php-embed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cscott","download_url":"https://codeload.github.com/cscott/node-php-embed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248401955,"owners_count":21097328,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T06:15:58.104Z","updated_at":"2025-04-11T12:31:21.364Z","avatar_url":"https://github.com/cscott.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"# php-embed\n[![NPM][NPM1]][NPM2]\n\n[![Build Status][1]][2] [![dependency status][3]][4] [![dev dependency status][5]][6]\n\nThe node `php-embed` package binds to PHP's \"embed SAPI\" in order to\nprovide bidirectional interoperability between PHP and JavaScript code\nin a single process.\n\nNode/iojs \u003e= 2.4.0 is currently required, since we use `NativeWeakMap`s\nin the implementation.  This could probably be worked around using\nv8 hidden properties, but it doesn't seem worth it right now.\n\n# Usage\n\n## Basic\n\n```js\nvar path = require('path');\nvar php = require('php-embed');\nphp.request({\n  file: path.join(__dirname, 'hello.php'),\n  stream: process.stdout\n}).then(function(v) {\n  console.log('php is done and stream flushed.');\n});\n```\n\n## Advanced\n\n```js\nvar php = require('php-embed');\nphp.request({\n  source: ['call_user_func(function() {',\n           '  class Foo {',\n           '    var $bar = \"bar\";',\n           '  }',\n           ' $c = $_SERVER[\"CONTEXT\"];',\n           ' // Invoke an Async JS method',\n           ' $result = $c-\u003ejsfunc(new Foo, $c-\u003ejsvalue, new Js\\\\Wait);',\n           '  // And return the value back to JS.',\n          '  return $result;',\n          '})'].join('\\n'),\n  context: {\n    jsvalue: 42, // Pass JS values to PHP\n    jsfunc: function(foo, value, cb) {\n      // Access PHP object from JS\n      console.log(foo.bar, value); // Prints \"bar 42\"\n      // Asynchronous completion, doesn't block node event loop\n      setTimeout(function() { cb(null, \"done\") }, 500);\n    }\n  }\n}).then(function(v) {\n  console.log(v); // Prints \"done\" ($result from PHP)\n}).done();\n```\n\n## Running command-line PHP scripts\n\nThe `php-embed` package contains a binary which can be used as a\ndrop-in replacement for the `php` CLI binary:\n\n```sh\nnpm install -g php-embed\nphp-embed some-file.php argument1 argument2....\n```\n\nNot every feature of the PHP CLI binary has been implemented; this\nis currently mostly a convenient testing tool.\n\n# API\n\n## php.request(options, [callback])\nTriggers a PHP \"request\", and returns a [`Promise`] which will be\nresolved when the request completes.  If you prefer to use callbacks,\nyou can ignore the return value and pass a callback as the second\nparameter.\n*   `options`: an object containing various parameters for the request.\n    Either `source` or `file` is mandatory; the rest are optional.\n    - `source`:\n        Specifies a source string to evaluate *as an expression* in\n        the request context.  (If you want to evaluate a statement,\n        you can wrap it in [`call_user_func`]`(function () { ... })`.)\n    - `file`:\n        Specifies a PHP file to evaluate in the request context.\n    - `stream`:\n        A node [`stream.Writable`] to accept output from the PHP\n        request.  If not specified, defaults to `process.stdout`.\n    - `request`:\n        If an [`http.IncomingMessage`] is provided here, the PHP\n        server variables will be set up with information about\n        the request.\n    - `args`:\n        If an array with at least one element is provided, the\n        PHP `$argc` and `$argv` variables will be set up as\n        PHP CLI programs expect.  Note that `args[0]` should\n        be the \"script file name\", as in C convention.\n    - `context`:\n        A JavaScript object which will be made available to the PHP\n        request in `$_SERVER['CONTEXT']`.\n    - `serverInitFunc`:\n        The user can provide a JavaScript function which will\n        be passed an object containing values for the PHP\n        [`$_SERVER`] variable, such as `REQUEST_URI`, `SERVER_ADMIN`, etc.\n        You can add or override values in this function as needed\n        to set up your request.\n*   `callback` *(optional)*: A standard node callback.  The first argument\n    is non-null iff an exception was raised. The second argument is the\n    result of the PHP evaluation, converted to a string.\n\n# PHP API\n\nFrom the PHP side, there are three new classes defined, all in the\n`Js` namespace, and one new property defined in the [`$_SERVER`]\nsuperglobal.\n\n## `$_SERVER['CONTEXT']`\nThis is the primary mechanism for passing data from the node\nprocess to the PHP request.  You can pass over a reference to\na JavaScript object, and populate it with whatever functions\nor data you wish to make available to the PHP code.\n\n## class `Js\\Object`\nThis is the class which wraps JavaScript objects visible to PHP code.\nYou can't create new objects of this type except by invoking\nJavaScript functions/methods/constructors.\n\n## class `Js\\Buffer`\nThis class wraps a PHP string to indicate that it should be passed to\nJavaScript as a node `Buffer` object, instead of decoded to UTF-8 and\nconverted to a JavaScript String.  Assuming that a node-style\nWritable stream is made available to PHP as `$stream`, compare:\n\n```php\n# The PHP string \"abc\" is decoded as UTF8 to form a JavaScript string,\n# which is then re-encoded as UTF8 and written to the stream:\n$stream.write(\"abc\", \"utf8\");\n# The PHP string \"abc\" is treated as a byte-stream and not de/encoded.\n$stream.write(new Js\\Buffer(\"abc\"));\n# Write to the stream synchronously (see description of next class)\n$stream.write(new Js\\Buffer(\"abc\"), new Js\\Wait());\n```\n\n## class `Js\\Wait`\nThis class allows you to invoke asynchronous JavaScript functions from\nPHP code as if they were synchronous.  You create a new instance of\n`Js\\Wait` and pass that to the function where it would expect a\nstandard node-style callback.  For example, if the JavaScript\n`setTimeout` function were made available to PHP as `$setTimeout`, then:\n```php\n$setTimeout(new Js\\Wait, 5000);\n```\nwould halt the PHP thread for 5 seconds.  More usefully, if you were\nto make the node [`fs`] module available to PHP as `$fs`, then:\n```php\n$contents = $fs.readFile('path/to/file', 'utf8', new Js\\Wait);\n```\nwould invoke the [`fs.readFile`] method asynchronously in the node context,\nbut block the PHP thread until its callback was invoked.  The result\nreturned in the callback would then be used as the return value for\nthe function invocation, resulting in `$contents` getting the result\nof reading the file.\n\nNote that calls using `Js\\Wait` block the PHP thread but do not\nblock the node thread.\n\n## class `Js\\ByRef`\nArguments are passed to JavaScript functions by value, as is the\ndefault in PHP.  This class allows you to pass arguments by reference;\nspecifically array values (since objects are effectively passed by\nreference already, and it does not apply to primitive values like\nstrings and integers).  Given the following JavaScript function\nmake available to PHP as `$jsfunc`:\n```js\nfunction jsfunc(arr) {\n  Array.prototype.push.call(arr, 4);\n}\n```\n\nYou could call in from PHP as follows:\n```php\n$a = array(1, 2, 3);\n$jsfunc($a);\nvar_dump($a);  # would still print (1, 2, 3)\n\n$jsfunc(new Js\\ByRef($a));\nvar_dump($a);  # now this would print (1, 2, 3, 4)\n```\n\n# Javascript API\n\n## PHP objects\nThe JavaScript `in` operator, when applied to a wrapped PHP object,\nworks the same as the PHP [`isset()`] function.  Similarly, when applied\nto a wrapped PHP object, JavaScript `delete` works like PHP [`unset()`].\n\n```js\nvar php = require('php-embed');\nphp.request({\n  source: 'call_user_func(function() {' +\n          '  class Foo { var $bar = null; var $bat = 42; } ' +\n          '  $_SERVER[\"CONTEXT\"](new Foo()); ' +\n          '})',\n  context: function(foo) {\n    console.log(\"bar\" in foo ? \"yes\" : \"no\"); // This prints \"no\"\n    console.log(\"bat\" in foo ? \"yes\" : \"no\"); // This prints \"yes\"\n  }\n}).done();\n```\n\nPHP has separate namespaces for properties and methods, while JavaScript\nhas just one.  Usually this isn't an issue, but if you need to you can use\na leading `$` to specify a property, or `__call` to specifically invoke a\nmethod.\n\n```js\nvar php = require('php-embed');\nphp.request({\n  source: ['call_user_func(function() {',\n           '  class Foo {',\n           '    var $bar = \"bar\";',\n           '    function bar($what) { echo \"I am a \", $what, \"!\\n\"; }',\n           '  }',\n           '  $foo = new Foo;',\n           '  // This prints \"bar\"',\n           '  echo $foo-\u003ebar, \"\\n\";',\n           '  // This prints \"I am a function!\"',\n           '  $foo-\u003ebar(\"function\");',\n           '  // Now try it in JavaScript',\n          '  $_SERVER[\"CONTEXT\"]($foo);',\n          '})'].join('\\n'),\n  context: function(foo) {\n    // This prints \"bar\"\n    console.log(foo.$bar);\n    // This prints \"I am a function\"\n    foo.__call(\"bar\", \"function\");\n  }\n}).done();\n```\n\n## PHP arrays\n\nPHP arrays are a sort of fusion of JavaScript arrays and objects.\nThey can store indexed data and have a sort of automatically-updated\n`length` property, like JavaScript arrays, but they can also store\nstring keys like JavaScript objects.\n\nIn JavaScript, we've decided to expose arrays as\n[array-like] [`Map`]s.\n\nThat is, they have the\n[`get`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get),\n[`set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set),\n[`delete`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete),\n[`keys`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys), and\n[`size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size)\nmethods of [`Map`].  These work as you'd expect, and access *all* the\nvalues in the PHP array, with both indexed and string keys.\n\nIn addition, as a convenience, they make the *indexed* keys (and only\nthe indexed keys) available as properties directly on the object,\nand export an appropriate `length` field.  This lets you use them\ndirectly in many JavaScript functions which accept \"array-like\"\nobjects.  For example, you can convert them easily to a \"true\"\nJavaScript array with [`Array.from`].\n\nArrays like objects are live-mapped: changes apply directly to\nthe PHP object they wrap.  However, note that arrays are by default\npassed *by value* to JavaScript functions; you may need to\nuse `Js\\ByRef` (see above) in order to have changes you make\non the JavaScript side affect the value of a PHP variable.\n\n## PHP ArrayAccess/Countable\nPHP objects which implement [`ArrayAccess`] and [`Countable`] are treated\nas PHP arrays, with the accessor methods described above.  However\nnote that the `length` property is fixed to `0` on these objects,\nsince there's no way to get a count of only the indexed keys\nin the array ([`Countable`] gives the count of *all* the keys,\ncounting both indexed and string keys).\n\n## Blocking the JavaScript event loop\n\nAt the moment, all property accesses and method invocations from\nJavaScript to PHP are done synchronously; that is, they block the\nJavaScript event loop.  The mechanisms are in place for asynchronous\naccess; I just haven't quite figured out what the syntax for that\nshould look like.\n\n# Installing\n\nYou can use [`npm`](https://github.com/isaacs/npm) to download and install:\n\n* The latest `php-embed` package: `npm install php-embed`\n\n* GitHub's `master` branch: `npm install https://github.com/cscott/node-php-embed/tarball/master`\n\nIn both cases the module is automatically built with npm's internal\nversion of `node-gyp`, and thus your system must meet\n[node-gyp's requirements](https://github.com/TooTallNate/node-gyp#installation).\n\nThe prebuilt binaries are built using g++-5 on Linux, and so you will\nneed to have the appropriate versions of the C++ standard library\navailable.  Something like `apt-get install g++-5` should suffice on\nDebian/Ubuntu.\n\nIt is also possible to make your own build of `php-embed` from its\nsource instead of its npm package ([see below](#building-from-the-source)).\n\n# Building from source\n\nUnless building via `npm install` you will need `node-pre-gyp`\ninstalled globally:\n\n    npm install -g node-pre-gyp\n\nThe `php-embed` module depends on the PHP embedding API.\nHowever, by default, an internal/bundled copy of `libphp5` will be built and\nstatically linked, so an externally installed `libphp5` is not required.\n\nIf you wish to install against an external `libphp5` then you need to\npass the `--libphp5` argument to `node-pre-gyp` or `npm install`.\n\n     node-pre-gyp --libphp5=external rebuild\n\nOr, using `npm`:\n\n     npm install --libphp5=external\n\nIf building against an external `libphp5` make sure to have the\ndevelopment headers available.  If you don't have them installed,\ninstall the `-dev` package with your package manager, e.g.\n`apt-get install libphp5-embed php5-dev` for Debian/Ubuntu.\nYour external `libphp5` should have been built with thread-safety\nenabled (`ZTS` turned on).\n\nYou will also need a C++11 compiler.  We perform builds using\nclang-3.5 and g++-5; both of these are known to work.  (Use\n`apt-get install g++-5` to install g++-5 if `g++ --version`\nreveals that you have an older version of `g++`.)  To ensure\nthat `npm`/`node-pre-gyp` use your preferred compiler, you may\nneed to do something like:\n\n```sh\nexport CXX=\"g++-5\"\nexport CC=\"gcc-5\"\n```\nOn Mac OSX, you need to limit support to OS X 10.7 and above in order\nto get C++11 support.  You will also need to install `libicu`.\nSomething like the following should work:\n\n```sh\nexport MACOSX_DEPLOYMENT_TARGET=10.7\nbrew install icu4c\n```\n\nDevelopers hacking on the code will probably want to use:\n\n    node-pre-gyp --debug build\n\nPassing the `--debug` flag to `node-pre-gyp` enables memory checking, and\nthe `build` command (instead of `rebuild`) avoids rebuilding `libphp5`\nfrom scratch after every change.  (You can also use `npm run\ndebug-build` if you find that easier to remember.)\n\n# Testing\n\nTo run the test suite, use:\n\n    npm test\n\nThis will run the JavaScript and C++ linters, as well as a test suite\nusing [mocha](https://github.com/visionmedia/mocha).\n\nDuring development, `npm run jscs-fix` will automatically correct most\nJavaScript code style issues, and `npm run valgrind` will detect a\nlarge number of potential memory issues.  Note that node itself will\nleak a small amount of memory from `node::CreateEnvironment`,\n`node::cares_wrap::Initialize`, and `node::Start`; these can safely be\nignored in the `valgrind` report.\n\n# Contributors\n\n* [C. Scott Ananian](https://github.com/cscott)\n\nMany thanks to [Sara Golemon](https://github.com/sgolemon) without whose\n[book](http://www.amazon.com/Extending-Embedding-PHP-Sara-Golemon/dp/067232704X/)\nthis project would have been impossible.\n\n# Related projects\n\n* [`mediawiki-express`](https://github.com/cscott/node-mediawiki-express)\n  is an npm package which uses `php-embed` to run mediawiki inside a\n  node.js [`express`](http://expressjs.com) server.\n* [`v8js`](https://github.com/preillyme/v8js) is a \"mirror image\"\n  project: it embeds the v8 JavaScript engine inside of PHP, whereas\n  `php-embed` embeds PHP inside node/v8.  The author of `php-embed`\n  is a contributor to `v8js` and they share bits of code.  The\n  JavaScript API to access PHP objects is deliberately similar\n  to that used by `v8js`.\n* [`dnode-php`](https://github.com/bergie/dnode-php) is an\n  RPC protocol implementation for Node and PHP, allowing calls\n  between Node and PHP code running on separate servers.  See\n  also [`require-php`](https://www.npmjs.com/package/require-php),\n  which creates the PHP server on the fly to provide a \"single server\"\n  experience similar to that of `php-embed`.\n* [`exec-php`](https://www.npmjs.com/package/exec-php) is another\n  clever embedding which uses the ability of the PHP CLI binary\n  to execute a single function in order to first export the\n  set of functions defined in a PHP file (using the\n  `_exec_php_get_user_functions` built-in) and then to implement\n  function invocation.\n\n# License\nCopyright (c) 2015 C. Scott Ananian.\n\n`php-embed` is licensed using the same\n[license](http://www.php.net/license/3_01.txt) as PHP itself.\n\n[`Promise`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\n[`call_user_func`]: http://php.net/manual/en/function.call-user-func.php\n[`stream.Writable`]: https://nodejs.org/api/stream.html#stream_class_stream_writable\n[`http.IncomingMessage`]: https://nodejs.org/api/http.html#http_http_incomingmessage\n[`$_SERVER`]: http://php.net/manual/en/reserved.variables.server.php\n[`fs`]: https://nodejs.org/api/fs.html\n[`fs.readFile`]: https://nodejs.org/api/fs.html#fs_fs_readfile_filename_options_callback\n[`isset()`]: http://php.net/manual/en/function.isset.php\n[`unset()`]: http://php.net/manual/en/function.unset.php\n[`ArrayAccess`]: http://php.net/manual/en/class.arrayaccess.php\n[`Countable`]: http://php.net/manual/en/class.countable.php\n\n[array-like]: http://www.2ality.com/2013/05/quirk-array-like-objects.html\n[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map\n[`Array.from`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from\n\n[NPM1]: https://nodei.co/npm/php-embed.png\n[NPM2]: https://nodei.co/npm/php-embed/\n\n[1]: https://travis-ci.org/cscott/node-php-embed.png\n[2]: https://travis-ci.org/cscott/node-php-embed\n[3]: https://david-dm.org/cscott/node-php-embed.png\n[4]: https://david-dm.org/cscott/node-php-embed\n[5]: https://david-dm.org/cscott/node-php-embed/dev-status.png\n[6]: https://david-dm.org/cscott/node-php-embed#info=devDependencies\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcscott%2Fnode-php-embed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcscott%2Fnode-php-embed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcscott%2Fnode-php-embed/lists"}