Skip to content

Commit 962b58a

Browse files
committed
Initial commit for jsphp engine, PHP 8.5 parser, runtime, extensions, CLI, and WordPress tests
0 parents  commit 962b58a

136 files changed

Lines changed: 10135 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules/
2+
tests/cache/
3+
*.zip
4+
wordpress/
5+
coverage/
6+
.cache/
7+
tests/latest.zip
8+
.idea/

‎AGENTS.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# AI Agent Guidelines for `node-jsphp`
2+
3+
This document provides operational context and guidance for AI agents working on the `node-jsphp` codebase.
4+
5+
## Core Directives
6+
7+
1. **Synchronous PHP / Asynchronous JS**:
8+
PHP code semantics appear synchronous to the PHP script, but generated JavaScript code MUST run asynchronously using `async/await` so Node.js can handle concurrent requests without blocking.
9+
10+
2. **Stack Trace Masking**:
11+
Never expose raw JavaScript execution stack frames to PHP. Standardize all stack traces in `PHPError` to reflect PHP line numbers or `[INTERNAL]`.
12+
13+
3. **No Build Output Directory for Sources**:
14+
TypeScript source files in `src/` must compile to JavaScript `.js` and declaration `.d.ts` files placed directly alongside their corresponding `.ts` sources. Do NOT use a `dist/` or `out/` folder.
15+
16+
4. **AST Optimization Rules**:
17+
Always ensure optimizer handles `extension_loaded(...)`, `defined(...)`, and boolean expressions at compile time. Eliminate unreachable `if` branches before generating JS output.
18+
19+
5. **WordPress & MySQL Compatibility**:
20+
Maintain full compatibility with WordPress database operations and standard themes.

‎README.md‎

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# jsphp (`node-jsphp`)
2+
3+
`jsphp` is a high-performance Node.js runtime, CLI, and transpiler engine for **PHP 8.5** written in TypeScript. It parses PHP code into ASTs, optimizes execution through constant folding and dead-code elimination, and transpiles PHP to asynchronous JavaScript source code with source maps (`.js.map`).
4+
5+
`jsphp` enables running PHP scripts, applications (including full **WordPress** sites), and command-line interfaces directly inside Node.js without needing native PHP binaries or socket connections to PHP-FPM.
6+
7+
---
8+
9+
## Features
10+
11+
- **PHP 8.5 Support**: Full support for PHP 8.5 language constructs, functions, and standard library.
12+
- **Asynchronous Execution Model**: All transpiled JavaScript code runs asynchronously via `async/await`, preventing blocking of Node.js event loop while executing PHP synchronously from PHP's perspective.
13+
- **Transparent Stack Trace Virtualization**: JavaScript stack traces are filtered and mapped to PHP file and line locations, displaying internal Node.js frames as `[INTERNAL]`.
14+
- **AST Optimization**: Precalculates constants, eliminates unreachable branches (`if(false)`, `if(extension_loaded(...))`, `defined(...)`), and folds expressions at compile time.
15+
- **On-Disk Transpilation Cache**: Saves compiled JavaScript files and sourcemaps to `process.env.JSPHP_CACHE` under `${JSPHP_CACHE}/${EngineConfigSHA1}/${FilePathSHA1}.js`.
16+
- **Live File Watching**: Uses `chokidar` to automatically invalidate and recompile cached files when PHP source files are updated.
17+
- **Built-in PHP Extensions**: Full implementations of `mysqli` (via `mysql2`), `pdo`, `pdo_mysql`, `gd` (via `sharp`), `pcre`, `mbstring`, `json`, `curl`, `session`, `xml`, `spl`, `hash`, `openssl`, and more.
18+
- **CLI & REPL**: `php` CLI tool supporting `-v`, `-r <code>`, `-a` interactive shell, and script execution.
19+
20+
---
21+
22+
## Installation
23+
24+
```bash
25+
npm install jsphp
26+
```
27+
28+
---
29+
30+
## Usage
31+
32+
### 1. Basic PHP Code Execution
33+
34+
```typescript
35+
import { PHPEngine } from "jsphp";
36+
37+
async function main() {
38+
const engine = new PHPEngine();
39+
const ctx = engine.createContext({
40+
stdout: (data) => process.stdout.write(data),
41+
});
42+
43+
await ctx.eval(`
44+
<?php
45+
$name = "JSPHP";
46+
echo "Hello, " . $name . "!\n";
47+
`);
48+
49+
engine.close();
50+
}
51+
52+
main();
53+
```
54+
55+
### 2. Running a PHP File
56+
57+
```typescript
58+
import { PHPContext } from "jsphp";
59+
60+
async function run() {
61+
await PHPContext.runFile("./index.php", {
62+
stdout: (data) => process.stdout.write(data),
63+
});
64+
}
65+
66+
run();
67+
```
68+
69+
### 3. Command Line Interface (CLI)
70+
71+
```bash
72+
# Print PHP version
73+
npx php -v
74+
75+
# Evaluate inline code
76+
npx php -r "echo 'Hello from CLI!';"
77+
78+
# Interactive REPL shell
79+
npx php -a
80+
81+
# Run script file
82+
npx php index.php
83+
```
84+
85+
---
86+
87+
## Architecture & Documentation
88+
89+
Detailed documentation can be found in the `docs/` directory:
90+
- [docs/architecture.md](docs/architecture.md): Overview of `PHPEngine`, `PHPContext`, and execution model.
91+
- [docs/transpiler.md](docs/transpiler.md): Details on AST parser, optimizer, sourcemaps, and cache hashing.
92+
- [docs/extensions.md](docs/extensions.md): Creating custom `PHPExtension` classes and registering functions/classes.
93+
94+
---
95+
96+
## Running Tests
97+
98+
```bash
99+
# Run unit tests
100+
npm test
101+
102+
# Run WordPress integration tests (requires local MySQL instance)
103+
MYSQL_ROOT_PASSWORD="DNESB*GJ*W(E$GYB$UW#gt78wg" npm test
104+
```
105+
106+
---
107+
108+
## License
109+
110+
[MIT](LICENSE.md)

‎bin/php-fpm.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env node
2+
const { runFPM } = require("../src/cli/php-fpm");
3+
4+
runFPM().catch((err) => {
5+
console.error(err);
6+
process.exit(1);
7+
});

‎bin/php.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env node
2+
const { runCLI } = require("../src/cli/php");
3+
4+
runCLI(process.argv.slice(2)).catch((err) => {
5+
console.error(err);
6+
process.exit(1);
7+
});

‎docs/architecture.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Architecture Overview
2+
3+
`jsphp` consists of three core layers:
4+
5+
1. **`PHPEngine`**:
6+
- Manages loaded extensions, core functions, constants, and classes.
7+
- Computes configuration SHA1 hash based on active extensions and defines.
8+
- Manages file watching via `chokidar` and compilation caching.
9+
10+
2. **`PHPContext`**:
11+
- Manages per-request runtime state, environment variables, working directory, output buffers, and superglobals (`$_GET`, `$_POST`, `$_SERVER`, etc.).
12+
- Executes compiled JS functions within its isolated scope.
13+
14+
3. **AST Parser, Optimizer & Transpiler**:
15+
- Parses PHP 8.5 code into an AST representation.
16+
- Optimizes constant expressions and dead code branches.
17+
- Transpiles AST nodes into JS functions taking a `PHPContext` argument.

‎docs/extensions.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Extension System
2+
3+
Extensions in `jsphp` extend the abstract class `PHPExtension`:
4+
5+
```typescript
6+
import { PHPExtension, PHPEngine, PHPContext } from "jsphp";
7+
8+
export class CustomExtension extends PHPExtension {
9+
public readonly name = "custom";
10+
11+
public onInit(engine: PHPEngine): void {
12+
this.constants = {
13+
CUSTOM_CONST: 42,
14+
};
15+
16+
this.functions = {
17+
custom_hello: (ctx: PHPContext, name: string) => {
18+
return `Hello, ${name}!`;
19+
},
20+
};
21+
}
22+
}
23+
```
24+
25+
Registering a custom extension:
26+
27+
```typescript
28+
const engine = new PHPEngine({
29+
extensions: [new CustomExtension()],
30+
});
31+
```

‎docs/transpiler.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# AST Optimizer & Transpiler
2+
3+
## Optimization Pipeline
4+
5+
1. **`PHPParser`**: Parses PHP source code into AST.
6+
2. **`ASTOptimizer`**:
7+
- Replaces `extension_loaded("ext")` calls with `true` or `false` based on engine configuration.
8+
- Replaces `defined("CONST")` with `true` or `false`.
9+
- Prunes `if (false)` branches.
10+
- Unwraps `if (true)` blocks.
11+
3. **`JSTranspiler`**:
12+
- Generates async JavaScript code using `ctx.getVar()`, `ctx.setVar()`, `ctx.echo()`, and `await ctx.callFunction()`.
13+
- Generates source maps (`.js.map`).
14+
- Writes compiled outputs to disk cache at `${process.env.JSPHP_CACHE}/${EngineSHA1}/${FilePathSHA1}.js`.

‎index.d.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export { PHPEngine, PHPEngineOptions } from "./src/PHPEngine";
2+
export { PHPContext, PHPContextOptions } from "./src/PHPContext";
3+
export { PHPExtension } from "./src/PHPExtension";
4+
export { PHPError, PHPException, PHPTypeError, PHPParseError, PHPFatalError } from "./src/runtime/errors/PHPError";
5+
export { PHPObject, PHPClass } from "./src/runtime/objects/PHPObject";
6+
export { Superglobals } from "./src/runtime/superglobals/Superglobals";
7+
export { OutputBufferStack } from "./src/runtime/output/OutputBuffer";
8+
export { PHPParser } from "./src/parser/PHPParser";
9+
export { ASTOptimizer } from "./src/parser/ASTOptimizer";
10+
export { JSTranspiler } from "./src/parser/JSTranspiler";

‎index.js‎

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)