@php-wasm/node 3.1.20 → 3.1.22

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/index.js CHANGED
@@ -25,6 +25,8 @@ async function getPHPLoaderModule(version = LatestSupportedPHPVersion) {
25
25
  return (await import("@php-wasm/node-8-0")).getPHPLoaderModule();
26
26
  case "7.4":
27
27
  return (await import("@php-wasm/node-7-4")).getPHPLoaderModule();
28
+ case "5.2":
29
+ return (await import("@php-wasm/node-5-2")).getPHPLoaderModule();
28
30
  }
29
31
  throw new Error(`Unsupported PHP version ${version}`);
30
32
  } catch (errorCandidate) {
@@ -389,8 +391,10 @@ async function withNetworking(phpModuleArgs = {}) {
389
391
  // packages/php-wasm/node/src/lib/load-runtime.ts
390
392
  import {
391
393
  loadPHPRuntime,
392
- FSHelpers as FSHelpers5,
394
+ FSHelpers,
393
395
  FileLockManagerComposite,
396
+ createLegacyPhpIniPreRunStep,
397
+ isLegacyPHPVersion,
394
398
  ProcessIdAllocator
395
399
  } from "@php-wasm/universal";
396
400
 
@@ -462,8 +466,8 @@ function bindUserSpace({ fileLockManager }, {
462
466
  [F_WRLCK]: "exclusive",
463
467
  [F_UNLCK]: "unlocked"
464
468
  },
465
- is_path_to_shared_fs(path2) {
466
- const { node } = FS.lookupPath(path2, { noent_okay: true });
469
+ is_path_to_shared_fs(path3) {
470
+ const { node } = FS.lookupPath(path3, { noent_okay: true });
467
471
  if (!node) {
468
472
  return false;
469
473
  }
@@ -1094,7 +1098,7 @@ function bindUserSpace({ fileLockManager }, {
1094
1098
  }
1095
1099
 
1096
1100
  // packages/php-wasm/node/src/lib/load-runtime.ts
1097
- import fs5 from "fs";
1101
+ import fs2 from "fs";
1098
1102
 
1099
1103
  // packages/php-wasm/node/src/lib/file-lock-manager-for-posix.ts
1100
1104
  import { MAX_ADDRESSABLE_FILE_OFFSET } from "@php-wasm/universal";
@@ -1112,7 +1116,7 @@ var FileLockManagerForPosix = class {
1112
1116
  this.wholeFileLockMap = /* @__PURE__ */ new Map();
1113
1117
  this.rangeLockedFds = /* @__PURE__ */ new Map();
1114
1118
  }
1115
- lockWholeFile(path2, op) {
1119
+ lockWholeFile(path3, op) {
1116
1120
  const opType = op.type === "unlock" ? "un" : op.waitForLock ? op.type === "exclusive" ? "ex" : "sh" : op.type === "exclusive" ? "exnb" : "shnb";
1117
1121
  try {
1118
1122
  flockSync(op.fd, opType);
@@ -1124,7 +1128,7 @@ var FileLockManagerForPosix = class {
1124
1128
  }
1125
1129
  this.wholeFileLockMap.get(op.pid).set(op.fd, {
1126
1130
  ...op,
1127
- path: path2
1131
+ path: path3
1128
1132
  });
1129
1133
  }
1130
1134
  return true;
@@ -1135,7 +1139,7 @@ var FileLockManagerForPosix = class {
1135
1139
  return false;
1136
1140
  }
1137
1141
  }
1138
- lockFileByteRange(path2, op, waitForLock) {
1142
+ lockFileByteRange(path3, op, waitForLock) {
1139
1143
  if (op.start === op.end) {
1140
1144
  op = {
1141
1145
  ...op,
@@ -1156,10 +1160,10 @@ var FileLockManagerForPosix = class {
1156
1160
  this.rangeLockedFds.set(op.pid, /* @__PURE__ */ new Map());
1157
1161
  }
1158
1162
  const pidMap = this.rangeLockedFds.get(op.pid);
1159
- if (!pidMap.has(path2)) {
1160
- pidMap.set(path2, /* @__PURE__ */ new Set());
1163
+ if (!pidMap.has(path3)) {
1164
+ pidMap.set(path3, /* @__PURE__ */ new Set());
1161
1165
  }
1162
- pidMap.get(path2).add(op.fd);
1166
+ pidMap.get(path3).add(op.fd);
1163
1167
  return true;
1164
1168
  } catch (e) {
1165
1169
  if (!isLockDenialError(e)) {
@@ -1168,13 +1172,13 @@ var FileLockManagerForPosix = class {
1168
1172
  return false;
1169
1173
  }
1170
1174
  }
1171
- findFirstConflictingByteRangeLock(path2, op) {
1175
+ findFirstConflictingByteRangeLock(path3, op) {
1172
1176
  if (op.type === "unlocked") {
1173
1177
  return void 0;
1174
1178
  }
1175
- const obtainedLock = this.lockFileByteRange(path2, op, false);
1179
+ const obtainedLock = this.lockFileByteRange(path3, op, false);
1176
1180
  if (obtainedLock) {
1177
- this.lockFileByteRange(path2, { ...op, type: "unlocked" }, true);
1181
+ this.lockFileByteRange(path3, { ...op, type: "unlocked" }, true);
1178
1182
  return void 0;
1179
1183
  }
1180
1184
  return {
@@ -1202,11 +1206,11 @@ var FileLockManagerForPosix = class {
1202
1206
  }
1203
1207
  this.wholeFileLockMap.delete(targetPid);
1204
1208
  }
1205
- for (const [path2, fdSet] of this.rangeLockedFds.get(targetPid) ?? []) {
1209
+ for (const [path3, fdSet] of this.rangeLockedFds.get(targetPid) ?? []) {
1206
1210
  for (const fd of fdSet) {
1207
1211
  try {
1208
1212
  this.lockFileByteRange(
1209
- path2,
1213
+ path3,
1210
1214
  {
1211
1215
  pid: targetPid,
1212
1216
  fd,
@@ -1218,7 +1222,7 @@ var FileLockManagerForPosix = class {
1218
1222
  );
1219
1223
  } catch (e) {
1220
1224
  logger2.error(
1221
- `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${fd} path=${path2}`,
1225
+ `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${fd} path=${path3}`,
1222
1226
  e
1223
1227
  );
1224
1228
  }
@@ -1282,7 +1286,7 @@ var FileLockManagerForWindows = class {
1282
1286
  this.wholeFileLockMap = /* @__PURE__ */ new Map();
1283
1287
  this.rangeLockedFds = /* @__PURE__ */ new Map();
1284
1288
  }
1285
- lockWholeFile(path2, op) {
1289
+ lockWholeFile(path3, op) {
1286
1290
  const start = 0n;
1287
1291
  const end = 2n ** 64n - 1n;
1288
1292
  if (op.type === "unlock") {
@@ -1294,7 +1298,7 @@ var FileLockManagerForWindows = class {
1294
1298
  }
1295
1299
  } else {
1296
1300
  logger3.warn(
1297
- `lockWholeFile: unlock failed for pid=${op.pid} fd=${op.fd} path=${path2}`
1301
+ `lockWholeFile: unlock failed for pid=${op.pid} fd=${op.fd} path=${path3}`
1298
1302
  );
1299
1303
  }
1300
1304
  return success2;
@@ -1362,22 +1366,22 @@ var FileLockManagerForWindows = class {
1362
1366
  }
1363
1367
  this.wholeFileLockMap.get(op.pid).set(op.fd, {
1364
1368
  ...op,
1365
- path: path2
1369
+ path: path3
1366
1370
  });
1367
1371
  }
1368
1372
  return success;
1369
1373
  }
1370
- lockFileByteRange(path2, op, waitForLock) {
1374
+ lockFileByteRange(path3, op, waitForLock) {
1371
1375
  if (op.start === op.end) {
1372
1376
  op = {
1373
1377
  ...op,
1374
1378
  end: MAX_ADDRESSABLE_FILE_OFFSET2
1375
1379
  };
1376
1380
  }
1377
- if (!this.rangeLockedFds.has(path2)) {
1378
- this.rangeLockedFds.set(path2, new FileLockIntervalTree());
1381
+ if (!this.rangeLockedFds.has(path3)) {
1382
+ this.rangeLockedFds.set(path3, new FileLockIntervalTree());
1379
1383
  }
1380
- const lockedRangeTree = this.rangeLockedFds.get(path2);
1384
+ const lockedRangeTree = this.rangeLockedFds.get(path3);
1381
1385
  const overlappingLocks = lockedRangeTree.findOverlapping(op);
1382
1386
  let preexistingLock;
1383
1387
  if (overlappingLocks.length === 1 && overlappingLocks[0].pid === op.pid && // NOTE: FD shouldn't matter for fcntl() F_SETLK because it is a process-level lock,
@@ -1462,12 +1466,12 @@ var FileLockManagerForWindows = class {
1462
1466
  return true;
1463
1467
  }
1464
1468
  }
1465
- findFirstConflictingByteRangeLock(path2, op) {
1469
+ findFirstConflictingByteRangeLock(path3, op) {
1466
1470
  if (op.type === "unlocked") {
1467
1471
  return void 0;
1468
1472
  }
1469
- const obtainedLock = !!this.lockFileByteRange(path2, op, false);
1470
- this.lockFileByteRange(path2, { ...op, type: "unlocked" }, false);
1473
+ const obtainedLock = !!this.lockFileByteRange(path3, op, false);
1474
+ this.lockFileByteRange(path3, { ...op, type: "unlocked" }, false);
1471
1475
  if (obtainedLock) {
1472
1476
  return void 0;
1473
1477
  }
@@ -1496,18 +1500,18 @@ var FileLockManagerForWindows = class {
1496
1500
  }
1497
1501
  this.wholeFileLockMap.delete(targetPid);
1498
1502
  }
1499
- for (const [path2, lockedRangeTree] of this.rangeLockedFds.entries()) {
1503
+ for (const [path3, lockedRangeTree] of this.rangeLockedFds.entries()) {
1500
1504
  const rangesLockedByTargetPid = lockedRangeTree.findLocksForProcess(targetPid);
1501
1505
  for (const op of rangesLockedByTargetPid) {
1502
1506
  try {
1503
1507
  this.lockFileByteRange(
1504
- path2,
1508
+ path3,
1505
1509
  { ...op, type: "unlocked" },
1506
1510
  false
1507
1511
  );
1508
1512
  } catch (e) {
1509
1513
  logger3.error(
1510
- `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${op.fd} path=${path2}`,
1514
+ `releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${op.fd} path=${path3}`,
1511
1515
  e
1512
1516
  );
1513
1517
  }
@@ -1543,204 +1547,63 @@ var FileLockManagerForWindows = class {
1543
1547
  }
1544
1548
  };
1545
1549
 
1546
- // packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
1550
+ // packages/php-wasm/node/src/lib/extensions/load-extensions.ts
1547
1551
  import { DEFAULT_IDE_KEY } from "@php-wasm/cli-util";
1548
1552
  import {
1549
- FSHelpers,
1550
- LatestSupportedPHPVersion as LatestSupportedPHPVersion3,
1551
- SupportedPHPVersions,
1552
- SupportedPHPVersionsList
1553
+ withResolvedPHPExtensions,
1554
+ resolvePHPExtension,
1555
+ SupportedPHPVersions
1553
1556
  } from "@php-wasm/universal";
1554
1557
  import fs from "fs";
1558
+ import path2 from "path";
1555
1559
 
1556
- // packages/php-wasm/node/src/lib/extensions/xdebug/get-xdebug-extension-module.ts
1560
+ // packages/php-wasm/node/src/lib/extensions/intl/get-intl-extension-module.ts
1557
1561
  import { LatestSupportedPHPVersion as LatestSupportedPHPVersion2 } from "@php-wasm/universal";
1558
- async function getXdebugExtensionModule(version = LatestSupportedPHPVersion2) {
1562
+ async function getIntlExtensionModule(version = LatestSupportedPHPVersion2) {
1559
1563
  switch (version) {
1560
1564
  case "8.5":
1561
- return (await import("@php-wasm/node-8-5")).getXdebugExtensionPath();
1565
+ return (await import("@php-wasm/node-8-5")).getIntlExtensionPath();
1562
1566
  case "8.4":
1563
- return (await import("@php-wasm/node-8-4")).getXdebugExtensionPath();
1567
+ return (await import("@php-wasm/node-8-4")).getIntlExtensionPath();
1564
1568
  case "8.3":
1565
- return (await import("@php-wasm/node-8-3")).getXdebugExtensionPath();
1569
+ return (await import("@php-wasm/node-8-3")).getIntlExtensionPath();
1566
1570
  case "8.2":
1567
- return (await import("@php-wasm/node-8-2")).getXdebugExtensionPath();
1571
+ return (await import("@php-wasm/node-8-2")).getIntlExtensionPath();
1568
1572
  case "8.1":
1569
- return (await import("@php-wasm/node-8-1")).getXdebugExtensionPath();
1573
+ return (await import("@php-wasm/node-8-1")).getIntlExtensionPath();
1570
1574
  case "8.0":
1571
- return (await import("@php-wasm/node-8-0")).getXdebugExtensionPath();
1575
+ return (await import("@php-wasm/node-8-0")).getIntlExtensionPath();
1572
1576
  case "7.4":
1573
- return (await import("@php-wasm/node-7-4")).getXdebugExtensionPath();
1577
+ return (await import("@php-wasm/node-7-4")).getIntlExtensionPath();
1574
1578
  }
1575
1579
  throw new Error(`Unsupported PHP version ${version}`);
1576
1580
  }
1577
1581
 
1578
- // packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
1579
- async function withXdebug(version = LatestSupportedPHPVersion3, options, xdebugOptions) {
1580
- const fileName = "xdebug.so";
1581
- const filePath = await getXdebugExtensionModule(version);
1582
- const extension = fs.readFileSync(filePath);
1583
- return {
1584
- ...options,
1585
- ENV: {
1586
- ...options.ENV,
1587
- PHP_INI_SCAN_DIR: "/internal/shared/extensions"
1588
- },
1589
- onRuntimeInitialized: (phpRuntime) => {
1590
- if (options.onRuntimeInitialized) {
1591
- options.onRuntimeInitialized(phpRuntime);
1592
- }
1593
- if (!FSHelpers.fileExists(
1594
- phpRuntime.FS,
1595
- "/internal/shared/extensions"
1596
- )) {
1597
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1598
- }
1599
- if (!FSHelpers.fileExists(
1600
- phpRuntime.FS,
1601
- `/internal/shared/extensions/${fileName}`
1602
- )) {
1603
- phpRuntime.FS.writeFile(
1604
- `/internal/shared/extensions/${fileName}`,
1605
- new Uint8Array(extension)
1606
- );
1607
- }
1608
- if (!FSHelpers.fileExists(
1609
- phpRuntime.FS,
1610
- "/internal/shared/extensions/xdebug.ini"
1611
- )) {
1612
- const ideKey = xdebugOptions.ideKey || DEFAULT_IDE_KEY;
1613
- phpRuntime.FS.writeFile(
1614
- "/internal/shared/extensions/xdebug.ini",
1615
- [
1616
- "zend_extension=/internal/shared/extensions/xdebug.so",
1617
- "xdebug.mode=debug,develop",
1618
- "xdebug.start_with_request=yes",
1619
- `xdebug.idekey="${ideKey}"`,
1620
- // Path mapping is only available starting
1621
- // from Xdebug 3.5, which is used by PHP 8.5+
1622
- // Previous versions will ignore this entry.
1623
- "xdebug.path_mapping=yes"
1624
- ].join("\n")
1625
- );
1626
- }
1627
- const isPHP85orHigher = SupportedPHPVersionsList.indexOf(version) <= SupportedPHPVersions.indexOf("8.5");
1628
- if (isPHP85orHigher) {
1629
- const { pathMappings, pathSkippings } = xdebugOptions;
1630
- if (!pathMappings && !pathSkippings)
1631
- return;
1632
- phpRuntime.FS.mkdir("/.xdebug");
1633
- if (pathMappings) {
1634
- phpRuntime.FS.writeFile(
1635
- "/.xdebug/path.map",
1636
- pathMappings.map((map) => `${map.vfsPath} = ${map.hostPath}`).join("\n")
1637
- );
1638
- }
1639
- if (pathSkippings) {
1640
- phpRuntime.FS.writeFile(
1641
- "/.xdebug/skip.map",
1642
- pathSkippings.map((path2) => `${path2} = SKIP`).join("\n")
1643
- );
1644
- }
1645
- }
1646
- }
1647
- };
1648
- }
1649
-
1650
- // packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
1651
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion5, FSHelpers as FSHelpers2 } from "@php-wasm/universal";
1652
- import fs2 from "fs";
1653
- import path from "path";
1654
-
1655
- // packages/php-wasm/node/src/lib/extensions/intl/get-intl-extension-module.ts
1656
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion4 } from "@php-wasm/universal";
1657
- async function getIntlExtensionModule(version = LatestSupportedPHPVersion4) {
1582
+ // packages/php-wasm/node/src/lib/extensions/memcached/get-memcached-extension-module.ts
1583
+ import { LatestSupportedPHPVersion as LatestSupportedPHPVersion3 } from "@php-wasm/universal";
1584
+ async function getMemcachedExtensionModule(version = LatestSupportedPHPVersion3) {
1658
1585
  switch (version) {
1659
1586
  case "8.5":
1660
- return (await import("@php-wasm/node-8-5")).getIntlExtensionPath();
1587
+ return (await import("@php-wasm/node-8-5")).getMemcachedExtensionPath();
1661
1588
  case "8.4":
1662
- return (await import("@php-wasm/node-8-4")).getIntlExtensionPath();
1589
+ return (await import("@php-wasm/node-8-4")).getMemcachedExtensionPath();
1663
1590
  case "8.3":
1664
- return (await import("@php-wasm/node-8-3")).getIntlExtensionPath();
1591
+ return (await import("@php-wasm/node-8-3")).getMemcachedExtensionPath();
1665
1592
  case "8.2":
1666
- return (await import("@php-wasm/node-8-2")).getIntlExtensionPath();
1593
+ return (await import("@php-wasm/node-8-2")).getMemcachedExtensionPath();
1667
1594
  case "8.1":
1668
- return (await import("@php-wasm/node-8-1")).getIntlExtensionPath();
1595
+ return (await import("@php-wasm/node-8-1")).getMemcachedExtensionPath();
1669
1596
  case "8.0":
1670
- return (await import("@php-wasm/node-8-0")).getIntlExtensionPath();
1597
+ return (await import("@php-wasm/node-8-0")).getMemcachedExtensionPath();
1671
1598
  case "7.4":
1672
- return (await import("@php-wasm/node-7-4")).getIntlExtensionPath();
1599
+ return (await import("@php-wasm/node-7-4")).getMemcachedExtensionPath();
1673
1600
  }
1674
1601
  throw new Error(`Unsupported PHP version ${version}`);
1675
1602
  }
1676
1603
 
1677
- // packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
1678
- async function withIntl(version = LatestSupportedPHPVersion5, options) {
1679
- const extensionName = "intl.so";
1680
- const extensionPath = await getIntlExtensionModule(version);
1681
- const extension = fs2.readFileSync(extensionPath);
1682
- const dataName = "icu.dat";
1683
- const moduleDir = typeof __dirname !== "undefined" ? __dirname : import.meta.dirname;
1684
- const dataPath = path.join(moduleDir, "shared", dataName);
1685
- const ICUData = fs2.readFileSync(dataPath);
1686
- return {
1687
- ...options,
1688
- ENV: {
1689
- ...options.ENV,
1690
- PHP_INI_SCAN_DIR: "/internal/shared/extensions",
1691
- ICU_DATA: "/internal/shared"
1692
- },
1693
- onRuntimeInitialized: (phpRuntime) => {
1694
- if (options.onRuntimeInitialized) {
1695
- options.onRuntimeInitialized(phpRuntime);
1696
- }
1697
- if (!FSHelpers2.fileExists(
1698
- phpRuntime.FS,
1699
- "/internal/shared/extensions"
1700
- )) {
1701
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1702
- }
1703
- if (!FSHelpers2.fileExists(
1704
- phpRuntime.FS,
1705
- `/internal/shared/extensions/${extensionName}`
1706
- )) {
1707
- phpRuntime.FS.writeFile(
1708
- `/internal/shared/extensions/${extensionName}`,
1709
- new Uint8Array(extension)
1710
- );
1711
- }
1712
- if (!FSHelpers2.fileExists(
1713
- phpRuntime.FS,
1714
- "/internal/shared/extensions/intl.ini"
1715
- )) {
1716
- phpRuntime.FS.writeFile(
1717
- "/internal/shared/extensions/intl.ini",
1718
- [
1719
- `extension=/internal/shared/extensions/${extensionName}`
1720
- ].join("\n")
1721
- );
1722
- }
1723
- if (!FSHelpers2.fileExists(
1724
- phpRuntime.FS,
1725
- `${phpRuntime.ENV.ICU_DATA}/${dataName}`
1726
- )) {
1727
- phpRuntime.FS.mkdirTree(phpRuntime.ENV.ICU_DATA);
1728
- phpRuntime.FS.writeFile(
1729
- `${phpRuntime.ENV.ICU_DATA}/icudt74l.dat`,
1730
- new Uint8Array(ICUData)
1731
- );
1732
- }
1733
- }
1734
- };
1735
- }
1736
-
1737
- // packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
1738
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion7, FSHelpers as FSHelpers3 } from "@php-wasm/universal";
1739
- import fs3 from "fs";
1740
-
1741
1604
  // packages/php-wasm/node/src/lib/extensions/redis/get-redis-extension-module.ts
1742
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion6 } from "@php-wasm/universal";
1743
- async function getRedisExtensionModule(version = LatestSupportedPHPVersion6) {
1605
+ import { LatestSupportedPHPVersion as LatestSupportedPHPVersion4 } from "@php-wasm/universal";
1606
+ async function getRedisExtensionModule(version = LatestSupportedPHPVersion4) {
1744
1607
  switch (version) {
1745
1608
  case "8.5":
1746
1609
  return (await import("@php-wasm/node-8-5")).getRedisExtensionPath();
@@ -1760,131 +1623,253 @@ async function getRedisExtensionModule(version = LatestSupportedPHPVersion6) {
1760
1623
  throw new Error(`Unsupported PHP version ${version}`);
1761
1624
  }
1762
1625
 
1763
- // packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
1764
- async function withRedis(version = LatestSupportedPHPVersion7, options) {
1765
- const extensionName = "redis.so";
1766
- const extensionPath = await getRedisExtensionModule(version);
1767
- const extension = fs3.readFileSync(extensionPath);
1768
- return {
1769
- ...options,
1770
- ENV: {
1771
- ...options.ENV,
1772
- PHP_INI_SCAN_DIR: "/internal/shared/extensions"
1773
- },
1774
- onRuntimeInitialized: (phpRuntime) => {
1775
- if (options.onRuntimeInitialized) {
1776
- options.onRuntimeInitialized(phpRuntime);
1777
- }
1778
- if (!FSHelpers3.fileExists(
1779
- phpRuntime.FS,
1780
- "/internal/shared/extensions"
1781
- )) {
1782
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1783
- }
1784
- if (!FSHelpers3.fileExists(
1785
- phpRuntime.FS,
1786
- `/internal/shared/extensions/${extensionName}`
1787
- )) {
1788
- phpRuntime.FS.writeFile(
1789
- `/internal/shared/extensions/${extensionName}`,
1790
- new Uint8Array(extension)
1791
- );
1792
- }
1793
- if (!FSHelpers3.fileExists(
1794
- phpRuntime.FS,
1795
- "/internal/shared/extensions/redis.ini"
1796
- )) {
1797
- phpRuntime.FS.writeFile(
1798
- "/internal/shared/extensions/redis.ini",
1799
- [
1800
- `extension=/internal/shared/extensions/${extensionName}`
1801
- ].join("\n")
1802
- );
1803
- }
1804
- }
1805
- };
1806
- }
1807
-
1808
- // packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
1809
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion9, FSHelpers as FSHelpers4 } from "@php-wasm/universal";
1810
- import fs4 from "fs";
1811
-
1812
- // packages/php-wasm/node/src/lib/extensions/memcached/get-memcached-extension-module.ts
1813
- import { LatestSupportedPHPVersion as LatestSupportedPHPVersion8 } from "@php-wasm/universal";
1814
- async function getMemcachedExtensionModule(version = LatestSupportedPHPVersion8) {
1626
+ // packages/php-wasm/node/src/lib/extensions/xdebug/get-xdebug-extension-module.ts
1627
+ import { LatestSupportedPHPVersion as LatestSupportedPHPVersion5 } from "@php-wasm/universal";
1628
+ async function getXdebugExtensionModule(version = LatestSupportedPHPVersion5) {
1815
1629
  switch (version) {
1816
1630
  case "8.5":
1817
- return (await import("@php-wasm/node-8-5")).getMemcachedExtensionPath();
1631
+ return (await import("@php-wasm/node-8-5")).getXdebugExtensionPath();
1818
1632
  case "8.4":
1819
- return (await import("@php-wasm/node-8-4")).getMemcachedExtensionPath();
1633
+ return (await import("@php-wasm/node-8-4")).getXdebugExtensionPath();
1820
1634
  case "8.3":
1821
- return (await import("@php-wasm/node-8-3")).getMemcachedExtensionPath();
1635
+ return (await import("@php-wasm/node-8-3")).getXdebugExtensionPath();
1822
1636
  case "8.2":
1823
- return (await import("@php-wasm/node-8-2")).getMemcachedExtensionPath();
1637
+ return (await import("@php-wasm/node-8-2")).getXdebugExtensionPath();
1824
1638
  case "8.1":
1825
- return (await import("@php-wasm/node-8-1")).getMemcachedExtensionPath();
1639
+ return (await import("@php-wasm/node-8-1")).getXdebugExtensionPath();
1826
1640
  case "8.0":
1827
- return (await import("@php-wasm/node-8-0")).getMemcachedExtensionPath();
1641
+ return (await import("@php-wasm/node-8-0")).getXdebugExtensionPath();
1828
1642
  case "7.4":
1829
- return (await import("@php-wasm/node-7-4")).getMemcachedExtensionPath();
1643
+ return (await import("@php-wasm/node-7-4")).getXdebugExtensionPath();
1830
1644
  }
1831
1645
  throw new Error(`Unsupported PHP version ${version}`);
1832
1646
  }
1833
1647
 
1834
- // packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
1835
- async function withMemcached(version = LatestSupportedPHPVersion9, options) {
1836
- const extensionName = "memcached.so";
1837
- const extensionPath = await getMemcachedExtensionModule(version);
1838
- const extension = fs4.readFileSync(extensionPath);
1648
+ // packages/php-wasm/node/src/lib/extensions/node-extension-resources.ts
1649
+ import { readFile } from "fs/promises";
1650
+ import path from "path";
1651
+ import { fileURLToPath, pathToFileURL } from "url";
1652
+ function normalizeNodeExtensionSource(source) {
1653
+ if (source.format === "url") {
1654
+ return {
1655
+ ...source,
1656
+ url: toNodeResourceUrl(source.url)
1657
+ };
1658
+ }
1659
+ if (source.format !== "manifest") {
1660
+ return source;
1661
+ }
1662
+ if ("manifest" in source) {
1663
+ return source.baseUrl ? {
1664
+ ...source,
1665
+ baseUrl: toNodeResourceUrl(source.baseUrl)
1666
+ } : source;
1667
+ }
1839
1668
  return {
1840
- ...options,
1841
- ENV: {
1842
- ...options.ENV,
1843
- PHP_INI_SCAN_DIR: "/internal/shared/extensions"
1844
- },
1845
- onRuntimeInitialized: (phpRuntime) => {
1846
- if (options.onRuntimeInitialized) {
1847
- options.onRuntimeInitialized(phpRuntime);
1848
- }
1849
- if (!FSHelpers4.fileExists(
1850
- phpRuntime.FS,
1851
- "/internal/shared/extensions"
1852
- )) {
1853
- phpRuntime.FS.mkdirTree("/internal/shared/extensions");
1854
- }
1855
- if (!FSHelpers4.fileExists(
1856
- phpRuntime.FS,
1857
- `/internal/shared/extensions/${extensionName}`
1858
- )) {
1859
- phpRuntime.FS.writeFile(
1860
- `/internal/shared/extensions/${extensionName}`,
1861
- new Uint8Array(extension)
1862
- );
1863
- }
1864
- if (!FSHelpers4.fileExists(
1865
- phpRuntime.FS,
1866
- "/internal/shared/extensions/memcached.ini"
1867
- )) {
1868
- phpRuntime.FS.writeFile(
1869
- "/internal/shared/extensions/memcached.ini",
1870
- [
1871
- `extension=/internal/shared/extensions/${extensionName}`
1872
- ].join("\n")
1873
- );
1874
- }
1669
+ ...source,
1670
+ manifestUrl: toNodeResourceUrl(source.manifestUrl)
1671
+ };
1672
+ }
1673
+ async function fetchNodeExtensionResource(input) {
1674
+ const url = input instanceof Request ? new URL(input.url) : input instanceof URL ? input : toNodeResourceUrl(String(input));
1675
+ if (url.protocol === "file:") {
1676
+ try {
1677
+ return new Response(await readFile(fileURLToPath(url)));
1678
+ } catch (error) {
1679
+ return new Response(String(error), {
1680
+ status: 404,
1681
+ statusText: "Not Found"
1682
+ });
1683
+ }
1684
+ }
1685
+ return fetch(input);
1686
+ }
1687
+ function toNodeResourceUrl(urlOrPath) {
1688
+ if (urlOrPath instanceof URL) {
1689
+ return urlOrPath;
1690
+ }
1691
+ try {
1692
+ const url = new URL(urlOrPath);
1693
+ if (url.protocol === "http:" || url.protocol === "https:" || url.protocol === "file:") {
1694
+ return url;
1875
1695
  }
1696
+ } catch {
1697
+ }
1698
+ return pathToFileURL(path.resolve(urlOrPath));
1699
+ }
1700
+
1701
+ // packages/php-wasm/node/src/lib/extensions/load-extensions.ts
1702
+ async function withPHPExtensions(version, asyncMode, options, extensions = []) {
1703
+ if (!extensions.length) {
1704
+ return options;
1705
+ }
1706
+ const resolvedExtensions = await Promise.all(
1707
+ extensions.map(
1708
+ (extension) => resolveRuntimePHPExtension(version, asyncMode, extension)
1709
+ )
1710
+ );
1711
+ return withResolvedPHPExtensions(options, resolvedExtensions);
1712
+ }
1713
+ async function resolveRuntimePHPExtension(version, asyncMode, extension) {
1714
+ if (typeof extension === "object" && "source" in extension) {
1715
+ return await resolvePHPExtension({
1716
+ ...extension,
1717
+ source: normalizeNodeExtensionSource(extension.source),
1718
+ phpVersion: version,
1719
+ asyncMode,
1720
+ fetch: extension.fetch ?? fetchNodeExtensionResource
1721
+ });
1722
+ }
1723
+ const builtIn = typeof extension === "string" ? { name: extension } : extension;
1724
+ switch (builtIn.name) {
1725
+ case "intl": {
1726
+ const extensionPath = await getIntlExtensionModule(version);
1727
+ const soBytes = new Uint8Array(fs.readFileSync(extensionPath));
1728
+ const dataName = "icu.dat";
1729
+ const moduleDir = typeof __dirname !== "undefined" ? __dirname : import.meta.dirname;
1730
+ const ICUData = fs.readFileSync(
1731
+ resolveIntlDataPath(moduleDir, dataName)
1732
+ );
1733
+ return await resolvePHPExtension({
1734
+ source: {
1735
+ format: "so",
1736
+ name: "intl",
1737
+ bytes: soBytes
1738
+ },
1739
+ phpVersion: version,
1740
+ asyncMode,
1741
+ env: {
1742
+ ICU_DATA: "/internal/shared"
1743
+ },
1744
+ extraFiles: {
1745
+ targetPath: "/internal/shared",
1746
+ files: {
1747
+ // The Intl extension looks for the hard-coded ICU data name.
1748
+ "icudt74l.dat": new Uint8Array(ICUData)
1749
+ }
1750
+ }
1751
+ });
1752
+ }
1753
+ case "redis": {
1754
+ const extensionPath = await getRedisExtensionModule(version);
1755
+ return await resolvePHPExtension({
1756
+ source: {
1757
+ format: "so",
1758
+ name: "redis",
1759
+ bytes: new Uint8Array(fs.readFileSync(extensionPath))
1760
+ },
1761
+ phpVersion: version,
1762
+ asyncMode
1763
+ });
1764
+ }
1765
+ case "memcached": {
1766
+ const extensionPath = await getMemcachedExtensionModule(version);
1767
+ return await resolvePHPExtension({
1768
+ source: {
1769
+ format: "so",
1770
+ name: "memcached",
1771
+ bytes: new Uint8Array(fs.readFileSync(extensionPath))
1772
+ },
1773
+ phpVersion: version,
1774
+ asyncMode
1775
+ });
1776
+ }
1777
+ case "xdebug": {
1778
+ const xdebugOptions = builtIn.options ?? {};
1779
+ const filePath = await getXdebugExtensionModule(version);
1780
+ const ideKey = xdebugOptions.ideKey || DEFAULT_IDE_KEY;
1781
+ return await resolvePHPExtension({
1782
+ source: {
1783
+ format: "so",
1784
+ name: "xdebug",
1785
+ bytes: new Uint8Array(fs.readFileSync(filePath))
1786
+ },
1787
+ phpVersion: version,
1788
+ asyncMode,
1789
+ loadWithIniDirective: "zend_extension",
1790
+ iniEntries: {
1791
+ "xdebug.mode": "debug,develop",
1792
+ "xdebug.start_with_request": "yes",
1793
+ "xdebug.idekey": `"${ideKey}"`,
1794
+ // Path mapping is only available starting from Xdebug 3.5,
1795
+ // which is used by PHP 8.5+. Previous versions ignore it.
1796
+ "xdebug.path_mapping": "yes"
1797
+ },
1798
+ extraFiles: resolveXdebugExtraFiles(version, xdebugOptions)
1799
+ });
1800
+ }
1801
+ default:
1802
+ throw new Error(
1803
+ `Unknown bundled PHP extension: ${String(builtIn.name)}.`
1804
+ );
1805
+ }
1806
+ }
1807
+ function resolveIntlDataPath(moduleDir, dataName) {
1808
+ const candidatePaths = [
1809
+ // Built package layout: dist/packages/php-wasm/node/shared/icu.dat.
1810
+ path2.join(moduleDir, "shared", dataName),
1811
+ // Source/test layout: src/lib/extensions/intl/shared/icu.dat.
1812
+ path2.join(moduleDir, "intl", "shared", dataName)
1813
+ ];
1814
+ const dataPath = candidatePaths.find(
1815
+ (candidate) => fs.existsSync(candidate)
1816
+ );
1817
+ if (!dataPath) {
1818
+ throw new Error(
1819
+ `Could not find ${dataName}. Checked: ${candidatePaths.join(", ")}`
1820
+ );
1821
+ }
1822
+ return dataPath;
1823
+ }
1824
+ function resolveXdebugExtraFiles(version, xdebugOptions) {
1825
+ const isPHP85orHigher = SupportedPHPVersions.indexOf(version) <= SupportedPHPVersions.indexOf("8.5");
1826
+ if (!isPHP85orHigher) {
1827
+ return void 0;
1828
+ }
1829
+ const { pathMappings, pathSkippings } = xdebugOptions;
1830
+ if (!pathMappings && !pathSkippings) {
1831
+ return void 0;
1832
+ }
1833
+ const files = {};
1834
+ if (pathMappings) {
1835
+ files["path.map"] = pathMappings.map((map) => `${map.vfsPath} = ${map.hostPath}`).join("\n");
1836
+ }
1837
+ if (pathSkippings) {
1838
+ files["skip.map"] = pathSkippings.map((path3) => `${path3} = SKIP`).join("\n");
1839
+ }
1840
+ return {
1841
+ targetPath: "/.xdebug",
1842
+ files
1876
1843
  };
1877
1844
  }
1878
1845
 
1879
1846
  // packages/php-wasm/node/src/lib/load-runtime.ts
1880
1847
  import { dirname, joinPaths, toPosixPath } from "@php-wasm/util";
1881
1848
  import { platform } from "os";
1849
+ import { jspi } from "wasm-feature-detect";
1882
1850
  var dangerousDefaultProcessIdAllocator = process.env.VITEST ? new ProcessIdAllocator() : void 0;
1883
1851
  async function loadNodeRuntime(phpVersion, options = {}) {
1884
1852
  const processId = options.emscriptenOptions?.processId ?? // !! Only assign a default process ID during test.
1885
1853
  // Otherwise, multiple workers with duplicate process IDs
1886
1854
  // could break file locking and lead to database corruption.
1887
1855
  (process.env.VITEST ? dangerousDefaultProcessIdAllocator.claim() : void 0);
1856
+ const isLegacy = isLegacyPHPVersion(phpVersion);
1857
+ const phpWasmAsyncMode = await jspi() ? "jspi" : "asyncify";
1858
+ const requestedExtensions = [...options.extensions ?? []];
1859
+ if (options.withIntl && !hasBuiltInExtension(requestedExtensions, "intl")) {
1860
+ requestedExtensions.push("intl");
1861
+ }
1862
+ if (options.withRedis && !hasBuiltInExtension(requestedExtensions, "redis")) {
1863
+ requestedExtensions.push("redis");
1864
+ }
1865
+ if (options.withMemcached && !hasBuiltInExtension(requestedExtensions, "memcached")) {
1866
+ requestedExtensions.push("memcached");
1867
+ }
1868
+ if (options.withXdebug && !hasBuiltInExtension(requestedExtensions, "xdebug")) {
1869
+ requestedExtensions.push(
1870
+ typeof options.withXdebug === "object" ? { name: "xdebug", options: options.withXdebug } : "xdebug"
1871
+ );
1872
+ }
1888
1873
  let emscriptenOptions = {
1889
1874
  /**
1890
1875
  * Emscripten default behavior is to kill the process when
@@ -1903,12 +1888,23 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1903
1888
  return bindUserSpace({ fileLockManager }, userSpaceContext);
1904
1889
  },
1905
1890
  ...options.emscriptenOptions || {},
1891
+ phpWasmAsyncMode,
1906
1892
  processId,
1893
+ // For legacy PHP: pre-create php.ini via a preRun step. See
1894
+ // createLegacyPhpIniPreRunStep for why this must run before
1895
+ // the PHP SAPI starts. Merge with any caller-provided preRun
1896
+ // hooks (the spread above may have set them).
1897
+ ...isLegacy ? {
1898
+ preRun: [
1899
+ createLegacyPhpIniPreRunStep(),
1900
+ ...options.emscriptenOptions?.preRun ?? []
1901
+ ]
1902
+ } : {},
1907
1903
  onRuntimeInitialized: (phpRuntime) => {
1908
1904
  if (options?.followSymlinks === true) {
1909
1905
  phpRuntime.FS.filesystems.NODEFS.node_ops.readlink = (node) => {
1910
1906
  const absoluteSourcePath = phpRuntime.FS.filesystems.NODEFS.tryFSOperation(
1911
- () => fs5.realpathSync(
1907
+ () => fs2.realpathSync(
1912
1908
  phpRuntime.FS.filesystems.NODEFS.realPath(node)
1913
1909
  )
1914
1910
  );
@@ -1917,9 +1913,9 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1917
1913
  `/internal/symlinks`,
1918
1914
  normalizedPath
1919
1915
  );
1920
- if (fs5.existsSync(absoluteSourcePath)) {
1921
- const sourceStat = fs5.statSync(absoluteSourcePath);
1922
- if (!FSHelpers5.fileExists(
1916
+ if (fs2.existsSync(absoluteSourcePath)) {
1917
+ const sourceStat = fs2.statSync(absoluteSourcePath);
1918
+ if (!FSHelpers.fileExists(
1923
1919
  phpRuntime.FS,
1924
1920
  symlinkMountPath
1925
1921
  )) {
@@ -1958,51 +1954,58 @@ async function loadNodeRuntime(phpVersion, options = {}) {
1958
1954
  phpRuntime.FS.root.mount.opts.root = ".";
1959
1955
  }
1960
1956
  };
1961
- if (options?.withXdebug) {
1962
- emscriptenOptions = await withXdebug(
1963
- phpVersion,
1964
- emscriptenOptions,
1965
- typeof options.withXdebug === "object" ? options.withXdebug : {}
1957
+ if (isLegacy && requestedExtensions.length) {
1958
+ throw new Error(
1959
+ `Extensions (xdebug, intl, redis, memcached) are not available for legacy PHP ${phpVersion}.`
1966
1960
  );
1967
1961
  }
1968
- if (options?.withIntl === true) {
1969
- emscriptenOptions = await withIntl(phpVersion, emscriptenOptions);
1970
- }
1971
- if (options?.withRedis === true) {
1972
- emscriptenOptions = await withRedis(phpVersion, emscriptenOptions);
1973
- }
1974
- if (options?.withMemcached === true) {
1975
- emscriptenOptions = await withMemcached(phpVersion, emscriptenOptions);
1962
+ if (!isLegacy) {
1963
+ const modernVersion = phpVersion;
1964
+ emscriptenOptions = await withPHPExtensions(
1965
+ modernVersion,
1966
+ phpWasmAsyncMode,
1967
+ emscriptenOptions,
1968
+ requestedExtensions
1969
+ );
1976
1970
  }
1977
1971
  emscriptenOptions = await withNetworking(emscriptenOptions);
1978
1972
  const phpLoaderModule = await getPHPLoaderModule(phpVersion);
1979
1973
  const runtimeId = await loadPHPRuntime(phpLoaderModule, emscriptenOptions);
1980
1974
  return runtimeId;
1981
1975
  }
1976
+ function hasBuiltInExtension(extensions, name) {
1977
+ return extensions.some((extension) => {
1978
+ if (typeof extension === "string") {
1979
+ return extension === name;
1980
+ }
1981
+ return !("source" in extension) && extension.name === name;
1982
+ });
1983
+ }
1982
1984
 
1983
1985
  // packages/php-wasm/node/src/lib/use-host-filesystem.ts
1984
- import { lstatSync as lstatSync2, readdirSync } from "node:fs";
1986
+ import { lstatSync, readdirSync } from "node:fs";
1985
1987
 
1986
1988
  // packages/php-wasm/node/src/lib/node-fs-mount.ts
1987
1989
  import {
1988
- FSHelpers as FSHelpers6
1990
+ FSHelpers as FSHelpers2
1989
1991
  } from "@php-wasm/universal";
1990
1992
  import { isParentOf } from "@php-wasm/util";
1991
- import { lstatSync } from "fs";
1993
+ import { realpathSync, statSync } from "fs";
1992
1994
  import { dirname as dirname2 } from "path";
1993
1995
  function createNodeFsMountHandler(localPath) {
1994
1996
  return function(php, FS, vfsMountPoint) {
1995
1997
  let removeVfsNode = false;
1996
- if (!FSHelpers6.fileExists(FS, vfsMountPoint)) {
1997
- const lstat = lstatSync(localPath);
1998
- if (lstat.isFile() || lstat.isSymbolicLink()) {
1998
+ const mountRoot = realpathSync(localPath);
1999
+ if (!FSHelpers2.fileExists(FS, vfsMountPoint)) {
2000
+ const stat = statSync(mountRoot);
2001
+ if (stat.isFile()) {
1999
2002
  FS.mkdirTree(dirname2(vfsMountPoint));
2000
2003
  FS.writeFile(vfsMountPoint, "");
2001
- } else if (lstat.isDirectory()) {
2004
+ } else if (stat.isDirectory()) {
2002
2005
  FS.mkdirTree(vfsMountPoint);
2003
2006
  } else {
2004
2007
  throw new Error(
2005
- "Unsupported file type. PHP-wasm supports only symlinks that link to files, directories, or symlinks."
2008
+ "Unsupported file type. PHP-wasm supports mounting only files and directories, including symlinks that resolve to files or directories."
2006
2009
  );
2007
2010
  }
2008
2011
  removeVfsNode = true;
@@ -2019,7 +2022,7 @@ function createNodeFsMountHandler(localPath) {
2019
2022
  }
2020
2023
  throw e;
2021
2024
  }
2022
- FS.mount(FS.filesystems["NODEFS"], { root: localPath }, vfsMountPoint);
2025
+ FS.mount(FS.filesystems["NODEFS"], { root: mountRoot }, vfsMountPoint);
2023
2026
  return () => {
2024
2027
  FS.unmount(vfsMountPoint);
2025
2028
  if (removeVfsNode) {
@@ -2055,20 +2058,20 @@ function useHostFilesystem(php) {
2055
2058
  }
2056
2059
  php.chdir(process.cwd());
2057
2060
  }
2058
- function statPathFollowSymlinks(path2) {
2059
- let stat = lstatSync2(path2);
2061
+ function statPathFollowSymlinks(path3) {
2062
+ let stat = lstatSync(path3);
2060
2063
  if (stat.isSymbolicLink()) {
2061
- const fs6 = __require("fs");
2062
- let target = path2;
2064
+ const fs3 = __require("fs");
2065
+ let target = path3;
2063
2066
  const seen = /* @__PURE__ */ new Set();
2064
2067
  while (true) {
2065
2068
  if (seen.has(target)) {
2066
- throw new Error(`Symlink loop detected: ${path2}`);
2069
+ throw new Error(`Symlink loop detected: ${path3}`);
2067
2070
  }
2068
2071
  seen.add(target);
2069
- const linkStat = lstatSync2(target);
2072
+ const linkStat = lstatSync(target);
2070
2073
  if (linkStat.isSymbolicLink()) {
2071
- target = fs6.realpathSync(target);
2074
+ target = fs3.realpathSync(target);
2072
2075
  continue;
2073
2076
  }
2074
2077
  stat = linkStat;
@@ -2085,6 +2088,5 @@ export {
2085
2088
  getPHPLoaderModule,
2086
2089
  loadNodeRuntime,
2087
2090
  useHostFilesystem,
2088
- withNetworking,
2089
- withXdebug
2091
+ withNetworking
2090
2092
  };