fix(build): embed vendor files across platforms and PHP versions

master
韩天峰 3 weeks ago
parent 94e2aee71d
commit cb548e5a2a
  1. 20
      README-CN.md
  2. 28
      README.md
  3. 2
      project.yml
  4. 34
      src/Build/ResourceCompilationTrait.php
  5. 105
      src/Build/SourcePipelineTrait.php
  6. 2
      src/Generator/Utils.php
  7. 34
      src/Translator.php
  8. 7
      src/compiler.php

@ -305,7 +305,7 @@ sources:
if: PHP_OS_FAMILY == "Windows"
# 将文件打包到二进制,并为 ZendVM 提供字节码及内存文件读取。
bundled-files:
embedded-files:
- vendor
# 由项目自身的原生构建流程预编译。
@ -337,19 +337,27 @@ ext-deps:
`PHP_VERSION`、`PHP_VERSION_ID` 和 `PHP_OS_FAMILY`。命令行参数优先于 YAML
中的同名配置。原生链接依赖应写入 `link-libs`;`ext-deps` 会生成
`ZEND_MOD_REQUIRED`,缺少所需 PHP 扩展时由 Zend 拒绝加载模块。
`bundled-files` 支持与 `sources` 相同的文件、目录及条件写法,仅在显式配置时启用。
`embedded-files` 支持与 `sources` 相同的文件、目录及条件写法,仅在显式配置时启用。
所列文件全部打包进二进制;未通过 `sources` 成功原生编译的 PHP 文件由 OPcache
生成字节码。运行时的 `require` 和 `require_once` 从内存交给 ZendVM 执行,
生成字节码。用于 API 声明的 `.stub.php` 文件仍保留在原始文件包中,不生成可执行字节码。
其他无法由 OPcache 编译的内嵌 PHP 文件会输出跳过日志,原始文件仍保留,但不进入
可执行字节码表。运行时的 `require` 和 `require_once` 从内存交给 ZendVM 执行,
无需读取磁盘上的 PHP 文件。构建字节码的 PHP CLI、OPcache 与目标 PHP 运行时需匹配。
因此使用 `embedded-files` 构建时,宿主机必须提供相应的 `php`/`php.exe` 和
OPcache 扩展;只有 `tpc` 无法生成字节码。
运行二进制文件时无需 Composer 安装、磁盘 vendor 文件或 OPcache 扩展;Composer
自动加载文件也已嵌入,仍按需加载类。构建时有 OPcache 的情况下,匿名类在首次执行
对应 `new class` 表达式时从同一字节码表加载。未配置 `bundled-files` 且缺少
对应 `new class` 表达式时从同一字节码表加载。未配置 `embedded-files` 且缺少
OPcache 时,匿名类退回内嵌 PHP 代码方式。
仅当目录名为 `vendor` 且目录下存在 `autoload.php` 时,才会使用字节码缓存;
目录 mtime 及构建用的 PHP/OPcache 未变化时复用字节码。其他 `bundled-files`
目录 mtime 及构建用的 PHP/OPcache 未变化时复用字节码。其他 `embedded-files`
文件(包括缺少 `autoload.php` 的同名目录)每次构建都重新生成。
修改目录下已有文件不会更新目录 mtime;这种情况可用 `--force` 重新生成
vendor 字节码。
vendor 字节码。不可执行的 vendor PHP 文件也会缓存跳过结果,`--force` 可重新尝试。
编译器生成的匿名类字节码按生成的 PHP 内容及构建用的 PHP/OPcache 单独缓存;
未变化的匿名类不会导致内嵌归档重新编译。
Windows 上,TypePHP 通过 `rc.exe` 把归档链接为 PE 资源,由 phpx 字节码 helper
读取;归档未变化时复用 `.res` 文件。
通用的 `objects` 列表会把已有 `.o`/`.obj` 文件直接加入链接步骤。TypePHP
不会重新编译这些文件;原生编译器、目标架构、编译参数和增量构建均由项目负责。
使用目标通用参数的原生文件仍应放入 `sources`;仅当某个编译单元需要不同参数且

@ -337,7 +337,7 @@ sources:
if: PHP_OS_FAMILY == "Windows"
# Embed files for ZendVM execution and virtual file reads.
bundled-files:
embedded-files:
- vendor
# Precompiled by the project's external native build.
@ -370,24 +370,36 @@ directory; conditional entries support `PHP_VERSION`, `PHP_VERSION_ID`, and
`PHP_OS_FAMILY`. CLI arguments override their YAML counterparts. Native linker
dependencies belong in `link-libs`; `ext-deps` writes `ZEND_MOD_REQUIRED`
entries so Zend can reject loading when a required PHP extension is missing.
`bundled-files` accepts files or directories with the same conditional syntax.
`embedded-files` accepts files or directories with the same conditional syntax.
It is opt-in for embedded binary builds: all listed files are packed into the
binary, and PHP files not successfully compiled from `sources` are stored as
OPcache bytecode. `require` and `require_once` load those scripts through
ZendVM without reading their PHP files from disk. The PHP CLI and OPcache used
to build the blobs must match the target PHP runtime. The resulting binary
OPcache bytecode. `.stub.php` API declaration files remain in the raw bundle
and are not compiled as executable bytecode. Other embedded PHP files that
OPcache cannot compile are reported and kept only as raw files; they cannot be
executed from the opcode table. `require` and `require_once` load scripts with
generated bytecode through ZendVM without reading their PHP files from disk.
The PHP CLI and OPcache used
to build the blobs must match the target PHP runtime. Building with
`embedded-files` therefore requires that CLI and its OPcache extension on the
build host; `tpc` alone cannot generate the blobs. The resulting binary
does not need Composer installation, vendor files, or an OPcache extension at
runtime: Composer's autoload files are embedded and still resolve classes on
demand. When OPcache is available at build time, anonymous classes use the
same opcode table when their `new class` expression is first executed. Without
`bundled-files`, builds lacking OPcache use embedded PHP code for anonymous
`embedded-files`, builds lacking OPcache use embedded PHP code for anonymous
classes instead.
Only a `vendor` directory containing `autoload.php` uses the opcode cache.
Its blobs are reused while the directory mtime and build PHP/OPcache remain
unchanged. All other `bundled-files` files, including files in a `vendor`
unchanged. All other `embedded-files` files, including files in a `vendor`
directory without `autoload.php`, are regenerated on every build. Directory
mtime does not change when an existing nested file is edited; use `--force`
to regenerate vendor opcodes in that case.
to regenerate vendor opcodes in that case. Skipped non-executable vendor PHP
files share this cache and are retried with `--force`.
Compiler-generated anonymous class opcodes have a separate cache keyed by
their generated PHP contents and the build PHP/OPcache, so unchanged classes
do not cause the embedded archive to be recompiled.
On Windows, TypePHP links the archive as a PE resource with `rc.exe` and reads
it through the phpx opcode helper. An unchanged archive reuses the `.res` file.
The generic `objects` list adds existing `.o`/`.obj` files directly to the
link step. TypePHP never recompiles these files; the project owns their native
compiler, architecture, flags, and incremental build. Keep native files that

@ -35,7 +35,7 @@ sources:
- vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.php
- vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php
bundled-files:
embedded-files:
- ./vendor
ignore:

@ -19,7 +19,7 @@ trait ResourceCompilationTrait
return false;
}
$generator = $this->createResourceGenerator();
return $generator !== null && $generator->hasResource();
return ($generator !== null && $generator->hasResource()) || $this->embeddedArchiveFile !== null;
}
public function getResourceRcFile(): string
@ -48,18 +48,39 @@ trait ResourceCompilationTrait
}
$generator = $this->createResourceGenerator();
if ($generator === null || !$generator->hasResource()) {
if (($generator === null || !$generator->hasResource()) && $this->embeddedArchiveFile === null) {
return;
}
$rcFile = $this->getResourceRcFile();
$rcContent = $generator->generate();
$rcContent = $generator?->generate() ?? '';
if ($this->embeddedArchiveFile !== null) {
$archive = $this->embeddedArchiveFile;
$archivePath = str_replace('\\', '/', $archive);
$rcContent .= '// archive-sha256: ' . hash_file('sha256', $archive) . PHP_EOL;
$rcContent .= '24103 RCDATA "' . addcslashes($archivePath, '"') . '"' . PHP_EOL;
}
$this->writeFile($rcFile, "\xEF\xBB\xBF" . $rcContent);
$this->climate->info('Generated resource file: ' . $rcFile);
$backend = $this->getCompilerBackend();
if ($backend instanceof Msvc) {
$resFile = $this->getResourceResFile();
$inputs = [$rcContent];
if ($generator !== null) {
foreach ([$generator->getIconPath(), $generator->getManifestPath()] as $file) {
if ($file !== null && is_file($file)) {
$inputs[] = hash_file('sha256', $file);
}
}
}
$cacheKey = hash('sha256', implode("\n", $inputs));
$cacheFile = $this->getBuildDir() . '/cache/resource/app_resource.sha256';
if (!$this->climate->arguments->defined('force') && is_file($resFile)
&& is_file($cacheFile) && trim((string) file_get_contents($cacheFile)) === $cacheKey) {
$this->climate->darkGray('Resource compiled: ' . $resFile . ' [cache]');
return;
}
$cmd = $backend->compileResourceFile($rcFile, $resFile);
$this->climate->comment($cmd);
@ -75,6 +96,13 @@ trait ResourceCompilationTrait
if (!file_exists($resFile)) {
$this->error('Resource file not generated: ' . $resFile);
}
if (!is_dir(dirname($cacheFile)) && !mkdir(dirname($cacheFile), 0777, true)
&& !is_dir(dirname($cacheFile))) {
throw new \RuntimeException('Cannot create resource cache directory: ' . dirname($cacheFile));
}
if (file_put_contents($cacheFile, $cacheKey . PHP_EOL) === false) {
throw new \RuntimeException('Cannot write resource cache metadata: ' . $cacheFile);
}
$this->climate->green('Resource compiled: ' . $resFile);
} else {
$this->climate->warning('Resource files are only supported with MSVC backend on Windows');

@ -23,10 +23,10 @@ trait SourcePipelineTrait
{
use PreparedProjectCacheTrait;
/** @var list<string> PHP files selected by bundled-files. */
/** @var list<string> PHP files selected by embedded-files. */
private array $bundledPhpFiles = [];
/** @var list<string> All regular files selected by bundled-files. */
/** @var list<string> All regular files selected by embedded-files. */
private array $bundledFiles = [];
/** @var list<string> Selected PHP files not translated to native code. */
@ -46,6 +46,13 @@ trait SourcePipelineTrait
private string $opcodeBuildPhpVersion = '';
private string $opcodeBuildSignature = '';
private ?string $embeddedArchiveFile = null;
private function getOpcodeBuildPhpCli(): string
{
return $this->getPhpDir() . ($this->isWindows() ? '/php.exe' : '/bin/php');
}
/**
* Prepare PHP inputs for the Composer php-nano source-composition build.
*
@ -197,18 +204,23 @@ trait SourcePipelineTrait
}
$this->opcodeBuildChecked = true;
$php = $this->getPhpDir() . '/bin/php';
$php = $this->getOpcodeBuildPhpCli();
if (!is_file($php) || !is_executable($php)) {
$this->opcodeBuildProbeError = "Build PHP CLI is not executable: {$php}";
return null;
}
foreach ([[], ['-d', 'zend_extension=opcache']] as $extensionArgs) {
$extensionCandidates = [[], ['-d', 'zend_extension=opcache']];
if ($this->isWindows()) {
$extensionCandidates[] = ['-d', 'zend_extension=' . $this->getPhpDir() . '/ext/php_opcache.dll'];
}
foreach ($extensionCandidates as $extensionArgs) {
$process = proc_open(
[
$php, '-n', ...$extensionArgs, '-d', 'opcache.enable_cli=1',
'-r', 'if (!extension_loaded("Zend OPcache") || !function_exists("opcache_compile_file")) exit(1); '
. '$extension = ini_get("extension_dir") . DIRECTORY_SEPARATOR . "opcache." . PHP_SHLIB_SUFFIX; '
. '$extension = PHP_OS_FAMILY === "Windows" ? dirname(PHP_BINARY) . "/ext/php_opcache.dll" '
. ': ini_get("extension_dir") . DIRECTORY_SEPARATOR . "opcache." . PHP_SHLIB_SUFFIX; '
. 'echo json_encode(["php" => PHP_VERSION, "opcache" => phpversion("Zend OPcache"), '
. '"binary" => [PHP_BINARY, hash_file("sha256", PHP_BINARY)], '
. '"extension" => [$extension, is_file($extension) ? hash_file("sha256", $extension) : null], '
@ -248,7 +260,7 @@ trait SourcePipelineTrait
if ($extensionArgs !== null) {
return $extensionArgs;
}
$php = $this->getPhpDir() . '/bin/php';
$php = $this->getOpcodeBuildPhpCli();
$detail = $this->opcodeBuildProbeError === '' ? '' : "\n" . $this->opcodeBuildProbeError;
$this->error("Embedded opcode generation requires Zend OPcache for the build PHP CLI: {$php}{$detail}");
}
@ -317,12 +329,31 @@ trait SourcePipelineTrait
}
// OPcache uses a different file-cache path layout on Windows.
if (DIRECTORY_SEPARATOR === '\\' && is_dir($cacheDir)) {
// The drive colon is omitted from OPcache's cache path (C:\foo
// becomes C\foo), after two system-specific cache directories.
$windowsCacheSuffix = str_replace('/', '\\', preg_replace('/^([A-Za-z]):/', '$1', $file)) . '.bin';
foreach (glob($cacheDir . '/*', GLOB_ONLYDIR) ?: [] as $systemDirectory) {
foreach (glob($systemDirectory . '/*', GLOB_ONLYDIR) ?: [] as $subDirectory) {
$candidate = $subDirectory . DIRECTORY_SEPARATOR . $windowsCacheSuffix;
if (is_file($candidate) && filesize($candidate) > 0) {
$matches[] = $candidate;
}
}
}
if (count($matches) > 1) {
throw new \RuntimeException("Ambiguous OPcache blob for {$file}");
}
if ($matches !== []) {
return $matches[0];
}
// Keep supporting other OPcache layouts without assuming the same
// number of system directories in every PHP build.
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($cacheDir, \FilesystemIterator::SKIP_DOTS),
);
foreach ($iterator as $cached) {
if ($cached->isFile() && $cached->getSize() > 0
&& str_ends_with($cached->getPathname(), $file . '.bin')) {
&& str_ends_with($cached->getPathname(), $windowsCacheSuffix)) {
if ($matches !== []) {
throw new \RuntimeException("Ambiguous OPcache blob for {$file}");
}
@ -333,6 +364,11 @@ trait SourcePipelineTrait
return $matches[0] ?? null;
}
private function getSkippedOpcodeMarker(string $cacheDir, string $file): string
{
return $cacheDir . '/skipped-' . hash('sha256', $file) . '.txt';
}
private function clearOpcodeCacheDirectory(string $directory): void
{
if (!is_dir($directory)) {
@ -375,7 +411,7 @@ trait SourcePipelineTrait
throw new \RuntimeException('Embedded opcodes require a native PHP build host matching the target');
}
$this->output('Generating embedded opcodes for ' . count($files) . ' PHP files', 'lightBlue');
$php = $this->getPhpDir() . '/bin/php';
$php = $this->getOpcodeBuildPhpCli();
$extensionArgs = $this->getOpcodeBuildExtensionArgs();
$phpVersion = $this->opcodeBuildPhpVersion;
$otherFiles = [];
@ -464,8 +500,14 @@ PHP
$pending = [];
$vendorHits = 0;
$anonymousHits = 0;
$vendorSkips = 0;
foreach ($files as $file) {
$cacheDir = $fileCacheDirs[$file];
if (isset($vendorFiles[$file])
&& is_file($this->getSkippedOpcodeMarker($cacheDir, $file))) {
++$vendorSkips;
continue;
}
if ((isset($vendorFiles[$file]) || isset($anonymousFiles[$file]))
&& ($blob = $this->findOpcodeBlob($cacheDir, $file)) !== null) {
$blobs[$file] = $blob;
@ -482,7 +524,8 @@ PHP
$vendorCount = array_sum(array_map('count', $vendorRoots));
$this->output(
'Vendor opcode cache: ' . $vendorHits . ' reused, '
. ($vendorCount - $vendorHits) . ' to generate',
. ($vendorCount - $vendorHits - $vendorSkips) . ' to generate'
. ($vendorSkips === 0 ? '' : ', ' . $vendorSkips . ' skipped'),
'lightBlue',
);
}
@ -512,9 +555,26 @@ PHP
fclose($pipes[1]);
fclose($pipes[2]);
if (proc_close($process) !== 0) {
throw new \RuntimeException("Cannot compile embedded opcode {$file}:\n{$stderr}");
if (isset($anonymousFiles[$file])
|| !str_contains((string) $stderr, "OPcache could not compile: {$file}")) {
throw new \RuntimeException("Cannot compile embedded opcode {$file}:\n{$stderr}");
}
// Embedded directories can contain PHP-looking declaration
// files that are not scripts. Keep their raw bytes but do
// not treat them as executable bytecode.
$this->climate->warning('Skipping non-executable embedded PHP file: ' . $file);
if (isset($vendorFiles[$file])) {
$marker = $this->getSkippedOpcodeMarker($cacheDir, $file);
if (file_put_contents($marker, $file . PHP_EOL) === false) {
throw new \RuntimeException("Cannot cache skipped embedded PHP file: {$file}");
}
}
$this->updateOpcodeProgress($progress, 'Opcodes', ++$completed, count($pending), $this->noProgress ? $file : null);
continue;
}
if (trim($stdout) !== $phpVersion) {
// Compiled files may emit PHP deprecation notices on stdout.
// The driver prints its version before compiling the file.
if (strtok((string) $stdout, "\r\n") !== $phpVersion) {
throw new \RuntimeException("Build PHP version changed while compiling {$file}");
}
$blob = $this->findOpcodeBlob($cacheDir, $file);
@ -580,13 +640,17 @@ PHP
} else {
unlink($temporaryArchive);
}
$assembly = $this->getBuildDir() . '/embedded-files-' . $this->targetName . '.S';
$quotedArchive = json_encode($archive, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
$archiveHash = hash_file('sha256', $archive);
$asmCode = "# archive-sha256: {$archiveHash}\n.section .rodata\n#ifdef __APPLE__\n.globl _typephp_embedded_archive_start\n.p2align 4\n_typephp_embedded_archive_start:\n#else\n.globl typephp_embedded_archive_start\n.p2align 4\ntypephp_embedded_archive_start:\n#endif\n.incbin {$quotedArchive}\n";
$this->writeFile($assembly, $asmCode);
$this->generatedProjectSources[$assembly] = true;
$sources[] = $assembly;
if ($this->isWindows()) {
$this->embeddedArchiveFile = $archive;
} else {
$assembly = $this->getBuildDir() . '/embedded-files-' . $this->targetName . '.S';
$quotedArchive = json_encode($archive, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
$asmCode = "# archive-sha256: {$archiveHash}\n.section .rodata\n#ifdef __APPLE__\n.globl _typephp_embedded_archive_start\n.p2align 4\n_typephp_embedded_archive_start:\n#else\n.globl typephp_embedded_archive_start\n.p2align 4\ntypephp_embedded_archive_start:\n#endif\n.incbin {$quotedArchive}\n";
$this->writeFile($assembly, $asmCode);
$this->generatedProjectSources[$assembly] = true;
$sources[] = $assembly;
}
$this->output(
'Packed ' . count($rawIndex) . ' files and ' . count($opcodeIndex) . ' opcode blobs',
'green',
@ -594,7 +658,10 @@ PHP
}
$code = '#include <typephp_opcode_table.h>' . PHP_EOL;
if ($this->bundledFiles !== [] || $files !== []) {
$hasArchive = $this->bundledFiles !== [] || $files !== [];
if ($hasArchive && $this->isWindows()) {
$code .= 'static const uint8_t *const typephp_embedded_archive_start = typephp_embedded_archive_data();' . PHP_EOL;
} elseif ($hasArchive) {
$code .= 'extern "C" const uint8_t typephp_embedded_archive_start[];' . PHP_EOL;
}
$code .= 'static const typephp_opcode_entry typephp_opcodes[] = {' . PHP_EOL;
@ -665,7 +732,7 @@ PHP
// take the highest precedence)
$this->applyCommandLineArguments();
if ($this->bundledFiles !== [] && !$this->isBuildModeBin()) {
$this->error('`bundled-files` requires `mode: bin`');
$this->error('`embedded-files` requires `mode: bin`');
}
if ($this->bundledFiles !== []) {
$this->getOpcodeBuildExtensionArgs();

@ -158,7 +158,7 @@ trait Utils
protected function escapeFileName(string $file): string
{
return str_replace('-', '_', $file);
return str_replace(['-', ':', '<', '>', '"', '|', '?', '*'], '_', $file);
}
protected function unescapeVarName(string $name): string

@ -1868,6 +1868,9 @@ CODE;
$phpxDir = $this->getPhpxDir();
$headerDirs = [$phpxDir . '/include', $phpxDir . '/src/misc'];
if (str_starts_with($cppFile, $phpxDir . '/thirdparty/opcache/')) {
$headerDirs[] = $phpxDir . '/thirdparty/opcache';
}
foreach ($headerDirs as $dir) {
if (!is_dir($dir)) {
@ -2021,7 +2024,10 @@ CODE;
public function isPhpxMiscFile(string $cppFile): bool
{
$miscDir = $this->getPhpxDir() . '/src/misc/';
return str_starts_with($cppFile, $miscDir);
$opcacheDir = $this->getPhpxDir() . '/thirdparty/opcache/';
return str_starts_with($cppFile, $miscDir)
|| (str_starts_with($cppFile, $opcacheDir)
&& preg_match('/\/opcode_unserialize_8[45]\.c$/', str_replace('\\', '/', $cppFile)) === 1);
}
/**
@ -2212,7 +2218,9 @@ CODE;
$sourceFiles[] = $this->getPhpxDir() . '/src/misc/typephp_main.cc';
if ($this->bundledFiles !== [] || $this->embeddedOpcodeFiles !== []) {
$sourceFiles[] = $this->getPhpxDir() . '/src/misc/typephp_opcode_table.cc';
$versionHeader = $this->getPhpDir() . '/include/php/main/php_version.h';
$versionHeader = $this->isWindows()
? $this->getPhpDir() . '/SDK/include/main/php_version.h'
: $this->getPhpDir() . '/include/php/main/php_version.h';
$versionText = is_file($versionHeader) ? file_get_contents($versionHeader) : '';
if (!preg_match('/#define PHP_VERSION_ID\s+(\d+)/', $versionText, $versionMatch)) {
throw new \RuntimeException("Cannot determine the target PHP version from {$versionHeader}");
@ -3866,23 +3874,26 @@ CODE;
$list = $this->getFilesFromDir($projectDir);
}
// Raw files are bundled; PHP files not emitted by `sources` are also
// Raw files are embedded; PHP files not emitted by `sources` are also
// compiled into OPcache blobs for ZendVM execution at runtime.
if (array_key_exists('opcode-sources', $cfg)) {
$this->error('`opcode-sources` has been renamed to `bundled-files`');
$this->error('`opcode-sources` has been renamed to `embedded-files`');
}
if (array_key_exists('bundled-files', $cfg)) {
if (!is_array($cfg['bundled-files'])) {
$this->error('`bundled-files` must be an array');
$this->error('`bundled-files` has been renamed to `embedded-files`');
}
if (array_key_exists('embedded-files', $cfg)) {
if (!is_array($cfg['embedded-files'])) {
$this->error('`embedded-files` must be an array');
}
foreach ($cfg['bundled-files'] as $entry) {
foreach ($cfg['embedded-files'] as $entry) {
[$src, $condition] = $this->parseProjectYamlSourceEntry($entry);
if ($condition !== null && !$this->evaluateProjectYamlCondition($condition)) {
continue;
}
$resolved = $this->getAbsolutePath($src, $projectDir);
if (!$resolved) {
$this->error('Bundled file or directory does not exist: `' . $src . '`');
$this->error('Embedded file or directory does not exist: `' . $src . '`');
}
if (is_file($resolved)) {
$this->bundledFiles[] = $resolved;
@ -3901,11 +3912,14 @@ CODE;
sort($this->bundledFiles, SORT_STRING);
$this->bundledPhpFiles = array_values(array_filter(
$this->bundledFiles,
static fn(string $file): bool => FileScanner::isPhpFile($file),
// PHP extension stubs are API declarations for code generators.
// They remain in the raw archive but must never be executed.
static fn(string $file): bool => FileScanner::isPhpFile($file)
&& !str_ends_with($file, '.stub.php'),
));
if ($this->bundledFiles !== []) {
$this->output(
'bundled-files: found ' . count($this->bundledFiles)
'embedded-files: found ' . count($this->bundledFiles)
. ' files (' . count($this->bundledPhpFiles) . ' PHP)',
'lightBlue',
);

@ -28,12 +28,7 @@ function main(int $argc, array $argv): void
);
}
// The Zend PHP entrypoint loads the consumer project's Composer autoloader
// in bin/bootstrap.php. The AOT compiler starts here directly and therefore
// must load the dependencies packaged alongside tpc itself.
if (!defined('TYPEPHP_PHP_SCRIPT_ENTRY')) {
require_once dirname(__DIR__) . '/vendor/autoload.php';
}
require_once dirname(__DIR__) . '/vendor/autoload.php';
$completionStatus = CompletionCommand::execute($argv);
if ($completionStatus !== null) {

Loading…
Cancel
Save