vite-plugin-php 0.9.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,11 @@ export default defineConfig({
15
15
  });
16
16
  ```
17
17
 
18
+ #### ⚠️ Attention!
19
+
20
+ Since version `^1.0.0` the plugin uses PHP's dev-server to compute PHP code and to provide all usual server variables and features.\
21
+ If you encounter a bug, use the prior `0.9.1` version of the plugin!
22
+
18
23
  ## Write some PHP code in your `index.php`
19
24
 
20
25
  ```php
package/dist/index.cjs CHANGED
@@ -1,27 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const child_process = require('child_process');
4
3
  const fs = require('fs');
5
4
  const path = require('path');
5
+ const http = require('http');
6
+ const child_process = require('child_process');
6
7
 
7
- function runPHP(input, args = {}) {
8
- const params = [runPHP.binary];
9
- Object.entries(args).forEach(([optionName, value]) => {
10
- if (optionName === "define") {
11
- Object.entries(value).forEach(([k, v]) => {
12
- params.push(`--${optionName} ${k}=${v}`);
13
- });
14
- } else {
15
- const name = optionName.replaceAll("_", "-");
16
- params.push(`--${name} ${value}`);
17
- }
18
- });
19
- let i = input;
20
- i = i.replaceAll("'", `'"'"'`);
21
- i = `echo '${i}' | ` + params.join(" ");
22
- return child_process.execSync(i).toString();
23
- }
24
- runPHP.binary = "php";
8
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
9
+
10
+ const http__default = /*#__PURE__*/_interopDefaultCompat(http);
25
11
 
26
12
  function makeID() {
27
13
  return Date.now().toString(36) + Math.random() * 100;
@@ -32,14 +18,9 @@ function writeFile(file, data) {
32
18
  fs.writeFileSync(file, data);
33
19
  }
34
20
 
35
- const codeTokens = /* @__PURE__ */ new Map();
36
21
  function escapePHP(inputFile, outputFile) {
37
- const fileId = path.resolve(outputFile);
38
22
  const input = fs.readFileSync(inputFile).toString();
39
- if (!codeTokens.has(fileId)) {
40
- codeTokens.set(fileId, /* @__PURE__ */ new Map());
41
- }
42
- const fileTokens = codeTokens.get(fileId);
23
+ const codeTokens = {};
43
24
  const isJS = inputFile.includes(".js") || inputFile.includes(".ts");
44
25
  const isML = inputFile.includes(".php") || inputFile.includes(".htm");
45
26
  const out = input.replaceAll(/<\?(?:php|).+?\?>/gi, (match) => {
@@ -49,45 +30,103 @@ function escapePHP(inputFile, outputFile) {
49
30
  } else if (isML) {
50
31
  token = `<!--${token}-->`;
51
32
  }
52
- fileTokens.set(token, match);
33
+ codeTokens[token] = match;
53
34
  return token;
54
35
  });
36
+ writeFile(outputFile + ".json", JSON.stringify(codeTokens));
55
37
  writeFile(outputFile, out);
56
38
  }
57
39
  function unescapePHP(file) {
58
- const fileId = path.resolve(file);
59
40
  const input = fs.readFileSync(file).toString();
60
- const fileTokens = codeTokens.get(fileId);
61
41
  let out = input;
62
- if (fileTokens) {
63
- fileTokens.forEach((code, token) => {
42
+ const tokensFile = file + ".json";
43
+ if (fs.existsSync(tokensFile)) {
44
+ const codeTokens = JSON.parse(fs.readFileSync(tokensFile).toString());
45
+ Object.entries(codeTokens).forEach(([token, code]) => {
64
46
  out = out.replace(token, (match) => {
65
- return code;
47
+ return `${code}`;
66
48
  });
67
49
  });
68
50
  }
69
51
  return out;
70
52
  }
71
53
 
54
+ function start(root) {
55
+ if (!globalThis.php?.pid) {
56
+ globalThis.php = child_process.spawn(phpServer.binary, [
57
+ "-S",
58
+ "localhost:" + phpServer.port,
59
+ "-t",
60
+ root,
61
+ path.resolve(__dirname + "/router.php")
62
+ ]).once("spawn", () => {
63
+ console.log(
64
+ `PHP development server started (PID: ${globalThis.php?.pid})`
65
+ );
66
+ }).on("error", (error) => {
67
+ console.error("PHP dev-server error: " + error.message);
68
+ }).on("close", (code) => {
69
+ console.log(`PHP development server stopped (Code: ${code})`);
70
+ });
71
+ }
72
+ }
73
+ function stop() {
74
+ if (globalThis.php) {
75
+ globalThis.php.stdin?.end();
76
+ globalThis.php.stdout?.destroy();
77
+ globalThis.php.stderr?.destroy();
78
+ if (globalThis.php.kill()) {
79
+ console.log("PHP development server killed");
80
+ } else {
81
+ console.error("Attention! Failed to kill PHP development server!");
82
+ }
83
+ }
84
+ }
85
+ const phpServer = {
86
+ binary: "php",
87
+ port: 65535,
88
+ start,
89
+ stop
90
+ };
91
+
72
92
  function usePHP(cfg = {}) {
73
93
  const {
74
94
  binary = "php",
75
95
  entry = "index.php",
76
- args,
77
- tempDir = ".php-tmp"
96
+ tempDir = ".php-tmp",
97
+ cleanup = {}
78
98
  } = cfg;
79
- runPHP.binary = binary;
99
+ const { dev: devCleanup = true } = cleanup;
100
+ phpServer.binary = binary;
80
101
  let config = void 0;
102
+ let viteServer = void 0;
81
103
  const entries = Array.isArray(entry) ? entry : [entry];
82
104
  function escapeFile(file) {
83
105
  const tempFile = `${tempDir}/${file}.html`;
84
106
  escapePHP(file, tempFile);
85
107
  return tempFile;
86
108
  }
87
- function cleanUp(dir = "") {
109
+ function cleanupTemp(dir = "") {
88
110
  const parentDir = dir ? dir + "/" : dir;
89
111
  fs.rmSync(parentDir + tempDir, { recursive: true, force: true });
90
112
  }
113
+ function onExit() {
114
+ if (config?.command === "serve") {
115
+ phpServer.stop();
116
+ devCleanup && cleanupTemp();
117
+ }
118
+ process.exit();
119
+ }
120
+ [
121
+ "exit",
122
+ "SIGINT",
123
+ "SIGUSR1",
124
+ "SIGUSR2",
125
+ "uncaughtException",
126
+ "SIGTERM"
127
+ ].forEach((eventType) => {
128
+ process.on(eventType, onExit.bind(null));
129
+ });
91
130
  return [
92
131
  {
93
132
  name: "prepare-php",
@@ -116,30 +155,53 @@ function usePHP(cfg = {}) {
116
155
  apply: "serve",
117
156
  enforce: "pre",
118
157
  configureServer(server) {
158
+ viteServer = server;
159
+ phpServer.start(viteServer?.config.root);
119
160
  server.middlewares.use(async (req, res, next) => {
120
- if (req.url) {
121
- const url = new URL(req.url, "http://localhost");
122
- let requestUrl = url.pathname;
123
- if (requestUrl.endsWith("/")) {
124
- requestUrl += "index.php";
125
- }
126
- requestUrl = requestUrl.substring(1);
127
- const entry2 = entries.find((file) => {
128
- return file === requestUrl || file.substring(0, file.lastIndexOf(".")) === requestUrl;
129
- });
130
- if (entry2) {
131
- let tempFile = `${tempDir}/`;
132
- tempFile += entry2 + ".html";
133
- if (fs.existsSync(path.resolve(tempFile))) {
134
- const code = unescapePHP(tempFile);
135
- const out = await server.transformIndexHtml(
136
- requestUrl || "/",
137
- runPHP(code, args)
138
- );
139
- res.end(out);
140
- return;
161
+ try {
162
+ if (req.url) {
163
+ const url = new URL(
164
+ req.url,
165
+ "http://localhost:" + phpServer.port
166
+ );
167
+ let requestUrl = url.pathname;
168
+ if (requestUrl.endsWith("/")) {
169
+ requestUrl += "index.php";
170
+ }
171
+ requestUrl = requestUrl.substring(1);
172
+ const entry2 = entries.find((file) => {
173
+ return file === requestUrl || file.substring(0, file.lastIndexOf(".")) === requestUrl;
174
+ });
175
+ if (entry2) {
176
+ const tempFile = `${tempDir}/${entry2}.html`;
177
+ if (fs.existsSync(path.resolve(tempFile))) {
178
+ url.pathname = tempFile;
179
+ const phpResult = await new Promise(
180
+ (resolve2, reject) => {
181
+ http__default.request(
182
+ url.toString(),
183
+ {
184
+ method: req.method,
185
+ headers: req.headers
186
+ },
187
+ (msg) => {
188
+ msg.on("data", resolve2);
189
+ }
190
+ ).on("error", reject).end();
191
+ }
192
+ );
193
+ let out = phpResult.toString();
194
+ out = await server.transformIndexHtml(
195
+ requestUrl || "/",
196
+ out
197
+ );
198
+ res.end(out);
199
+ return;
200
+ }
141
201
  }
142
202
  }
203
+ } catch (error) {
204
+ console.error(`Error: ${error}`);
143
205
  }
144
206
  next();
145
207
  });
@@ -155,9 +217,6 @@ function usePHP(cfg = {}) {
155
217
  path: "*"
156
218
  });
157
219
  }
158
- },
159
- buildEnd(error) {
160
- cleanUp();
161
220
  }
162
221
  },
163
222
  {
@@ -174,7 +233,7 @@ function usePHP(cfg = {}) {
174
233
  const code = unescapePHP(`${tempDir}/${file}.html`);
175
234
  writeFile(`${distDir}/${file}`, code);
176
235
  });
177
- cleanUp(distDir);
236
+ cleanupTemp(distDir);
178
237
  }
179
238
  }
180
239
  ];
package/dist/index.d.ts CHANGED
@@ -1,23 +1,13 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- type PHP_CLI_Args = {
4
- no_chdir?: boolean;
5
- no_header?: boolean;
6
- php_ini?: string;
7
- no_php_ini?: boolean;
8
- define?: Record<string, string>;
9
- profile_info?: boolean;
10
- process_begin?: string;
11
- process_code?: string;
12
- process_file?: string;
13
- process_end?: string;
14
- };
15
-
16
3
  type UsePHPConfig = {
17
4
  binary?: string;
18
5
  entry?: string | string[];
19
- args?: PHP_CLI_Args;
20
6
  tempDir?: string;
7
+ cleanup?: {
8
+ dev?: boolean;
9
+ build?: boolean;
10
+ };
21
11
  };
22
12
  declare function usePHP(cfg?: UsePHPConfig): Plugin[];
23
13
 
package/dist/index.mjs CHANGED
@@ -1,25 +1,7 @@
1
- import { execSync } from 'child_process';
2
1
  import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'fs';
3
2
  import { dirname, resolve } from 'path';
4
-
5
- function runPHP(input, args = {}) {
6
- const params = [runPHP.binary];
7
- Object.entries(args).forEach(([optionName, value]) => {
8
- if (optionName === "define") {
9
- Object.entries(value).forEach(([k, v]) => {
10
- params.push(`--${optionName} ${k}=${v}`);
11
- });
12
- } else {
13
- const name = optionName.replaceAll("_", "-");
14
- params.push(`--${name} ${value}`);
15
- }
16
- });
17
- let i = input;
18
- i = i.replaceAll("'", `'"'"'`);
19
- i = `echo '${i}' | ` + params.join(" ");
20
- return execSync(i).toString();
21
- }
22
- runPHP.binary = "php";
3
+ import http from 'http';
4
+ import { spawn } from 'child_process';
23
5
 
24
6
  function makeID() {
25
7
  return Date.now().toString(36) + Math.random() * 100;
@@ -30,14 +12,9 @@ function writeFile(file, data) {
30
12
  writeFileSync(file, data);
31
13
  }
32
14
 
33
- const codeTokens = /* @__PURE__ */ new Map();
34
15
  function escapePHP(inputFile, outputFile) {
35
- const fileId = resolve(outputFile);
36
16
  const input = readFileSync(inputFile).toString();
37
- if (!codeTokens.has(fileId)) {
38
- codeTokens.set(fileId, /* @__PURE__ */ new Map());
39
- }
40
- const fileTokens = codeTokens.get(fileId);
17
+ const codeTokens = {};
41
18
  const isJS = inputFile.includes(".js") || inputFile.includes(".ts");
42
19
  const isML = inputFile.includes(".php") || inputFile.includes(".htm");
43
20
  const out = input.replaceAll(/<\?(?:php|).+?\?>/gi, (match) => {
@@ -47,45 +24,103 @@ function escapePHP(inputFile, outputFile) {
47
24
  } else if (isML) {
48
25
  token = `<!--${token}-->`;
49
26
  }
50
- fileTokens.set(token, match);
27
+ codeTokens[token] = match;
51
28
  return token;
52
29
  });
30
+ writeFile(outputFile + ".json", JSON.stringify(codeTokens));
53
31
  writeFile(outputFile, out);
54
32
  }
55
33
  function unescapePHP(file) {
56
- const fileId = resolve(file);
57
34
  const input = readFileSync(file).toString();
58
- const fileTokens = codeTokens.get(fileId);
59
35
  let out = input;
60
- if (fileTokens) {
61
- fileTokens.forEach((code, token) => {
36
+ const tokensFile = file + ".json";
37
+ if (existsSync(tokensFile)) {
38
+ const codeTokens = JSON.parse(readFileSync(tokensFile).toString());
39
+ Object.entries(codeTokens).forEach(([token, code]) => {
62
40
  out = out.replace(token, (match) => {
63
- return code;
41
+ return `${code}`;
64
42
  });
65
43
  });
66
44
  }
67
45
  return out;
68
46
  }
69
47
 
48
+ function start(root) {
49
+ if (!globalThis.php?.pid) {
50
+ globalThis.php = spawn(phpServer.binary, [
51
+ "-S",
52
+ "localhost:" + phpServer.port,
53
+ "-t",
54
+ root,
55
+ resolve(__dirname + "/router.php")
56
+ ]).once("spawn", () => {
57
+ console.log(
58
+ `PHP development server started (PID: ${globalThis.php?.pid})`
59
+ );
60
+ }).on("error", (error) => {
61
+ console.error("PHP dev-server error: " + error.message);
62
+ }).on("close", (code) => {
63
+ console.log(`PHP development server stopped (Code: ${code})`);
64
+ });
65
+ }
66
+ }
67
+ function stop() {
68
+ if (globalThis.php) {
69
+ globalThis.php.stdin?.end();
70
+ globalThis.php.stdout?.destroy();
71
+ globalThis.php.stderr?.destroy();
72
+ if (globalThis.php.kill()) {
73
+ console.log("PHP development server killed");
74
+ } else {
75
+ console.error("Attention! Failed to kill PHP development server!");
76
+ }
77
+ }
78
+ }
79
+ const phpServer = {
80
+ binary: "php",
81
+ port: 65535,
82
+ start,
83
+ stop
84
+ };
85
+
70
86
  function usePHP(cfg = {}) {
71
87
  const {
72
88
  binary = "php",
73
89
  entry = "index.php",
74
- args,
75
- tempDir = ".php-tmp"
90
+ tempDir = ".php-tmp",
91
+ cleanup = {}
76
92
  } = cfg;
77
- runPHP.binary = binary;
93
+ const { dev: devCleanup = true } = cleanup;
94
+ phpServer.binary = binary;
78
95
  let config = void 0;
96
+ let viteServer = void 0;
79
97
  const entries = Array.isArray(entry) ? entry : [entry];
80
98
  function escapeFile(file) {
81
99
  const tempFile = `${tempDir}/${file}.html`;
82
100
  escapePHP(file, tempFile);
83
101
  return tempFile;
84
102
  }
85
- function cleanUp(dir = "") {
103
+ function cleanupTemp(dir = "") {
86
104
  const parentDir = dir ? dir + "/" : dir;
87
105
  rmSync(parentDir + tempDir, { recursive: true, force: true });
88
106
  }
107
+ function onExit() {
108
+ if (config?.command === "serve") {
109
+ phpServer.stop();
110
+ devCleanup && cleanupTemp();
111
+ }
112
+ process.exit();
113
+ }
114
+ [
115
+ "exit",
116
+ "SIGINT",
117
+ "SIGUSR1",
118
+ "SIGUSR2",
119
+ "uncaughtException",
120
+ "SIGTERM"
121
+ ].forEach((eventType) => {
122
+ process.on(eventType, onExit.bind(null));
123
+ });
89
124
  return [
90
125
  {
91
126
  name: "prepare-php",
@@ -114,30 +149,53 @@ function usePHP(cfg = {}) {
114
149
  apply: "serve",
115
150
  enforce: "pre",
116
151
  configureServer(server) {
152
+ viteServer = server;
153
+ phpServer.start(viteServer?.config.root);
117
154
  server.middlewares.use(async (req, res, next) => {
118
- if (req.url) {
119
- const url = new URL(req.url, "http://localhost");
120
- let requestUrl = url.pathname;
121
- if (requestUrl.endsWith("/")) {
122
- requestUrl += "index.php";
123
- }
124
- requestUrl = requestUrl.substring(1);
125
- const entry2 = entries.find((file) => {
126
- return file === requestUrl || file.substring(0, file.lastIndexOf(".")) === requestUrl;
127
- });
128
- if (entry2) {
129
- let tempFile = `${tempDir}/`;
130
- tempFile += entry2 + ".html";
131
- if (existsSync(resolve(tempFile))) {
132
- const code = unescapePHP(tempFile);
133
- const out = await server.transformIndexHtml(
134
- requestUrl || "/",
135
- runPHP(code, args)
136
- );
137
- res.end(out);
138
- return;
155
+ try {
156
+ if (req.url) {
157
+ const url = new URL(
158
+ req.url,
159
+ "http://localhost:" + phpServer.port
160
+ );
161
+ let requestUrl = url.pathname;
162
+ if (requestUrl.endsWith("/")) {
163
+ requestUrl += "index.php";
164
+ }
165
+ requestUrl = requestUrl.substring(1);
166
+ const entry2 = entries.find((file) => {
167
+ return file === requestUrl || file.substring(0, file.lastIndexOf(".")) === requestUrl;
168
+ });
169
+ if (entry2) {
170
+ const tempFile = `${tempDir}/${entry2}.html`;
171
+ if (existsSync(resolve(tempFile))) {
172
+ url.pathname = tempFile;
173
+ const phpResult = await new Promise(
174
+ (resolve2, reject) => {
175
+ http.request(
176
+ url.toString(),
177
+ {
178
+ method: req.method,
179
+ headers: req.headers
180
+ },
181
+ (msg) => {
182
+ msg.on("data", resolve2);
183
+ }
184
+ ).on("error", reject).end();
185
+ }
186
+ );
187
+ let out = phpResult.toString();
188
+ out = await server.transformIndexHtml(
189
+ requestUrl || "/",
190
+ out
191
+ );
192
+ res.end(out);
193
+ return;
194
+ }
139
195
  }
140
196
  }
197
+ } catch (error) {
198
+ console.error(`Error: ${error}`);
141
199
  }
142
200
  next();
143
201
  });
@@ -153,9 +211,6 @@ function usePHP(cfg = {}) {
153
211
  path: "*"
154
212
  });
155
213
  }
156
- },
157
- buildEnd(error) {
158
- cleanUp();
159
214
  }
160
215
  },
161
216
  {
@@ -172,7 +227,7 @@ function usePHP(cfg = {}) {
172
227
  const code = unescapePHP(`${tempDir}/${file}.html`);
173
228
  writeFile(`${distDir}/${file}`, code);
174
229
  });
175
- cleanUp(distDir);
230
+ cleanupTemp(distDir);
176
231
  }
177
232
  }
178
233
  ];
@@ -0,0 +1,15 @@
1
+ <?php
2
+ $sourceFile = $_SERVER['SCRIPT_FILENAME'];
3
+
4
+ $source = file_get_contents($sourceFile);
5
+ $codeTokens = json_decode(file_get_contents("$sourceFile.json"), true);
6
+
7
+ $source = str_replace(
8
+ array_keys($codeTokens),
9
+ array_values($codeTokens),
10
+ $source,
11
+ );
12
+
13
+ die((function ($__SOURCE) {
14
+ eval("?>$__SOURCE<?php");
15
+ })($source));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-php",
3
- "version": "0.9.1",
3
+ "version": "1.0.1",
4
4
  "description": "Precompile PHP-files with the speed of Vite",
5
5
  "keywords": [
6
6
  "vite",