123 lines
4.5 KiB
PHP
Executable File
123 lines
4.5 KiB
PHP
Executable File
#!/usr/bin/env php
|
|
<?php
|
|
/**
|
|
* phantasm is a tool to assemble information about the codebase and then aggressively optimize it.
|
|
* This modifies the files that would be analyzed by Phan.
|
|
*
|
|
* Currently, the only optimization step this performs is replacing class constants with simple values.
|
|
* (Opcache can only replace class constants within the class declaring the constant, so that helps with that)
|
|
*
|
|
* See tests/phantasm_test for examples of the output.
|
|
*
|
|
* Future plans:
|
|
*
|
|
* - Expand the supported types of expressions that can be replaced or used as replacements
|
|
* - Inline instance and method calls across files (e.g. methods returning constants or simple expressions)
|
|
* - Add option to inline getters/setters, where the real type is known and there are no subclasses (by default, assume the original won't throw a TypeError)
|
|
* - Add ways to integrate this with building a phar file (unsafe)
|
|
* - Integrate other optimizations Phan can already do, such as --automatic-fix for NotFullyQualifiedUsagePlugin.
|
|
* - Use Phan's signature map/documentation map information about which constants, classes, and functions/methods are internal
|
|
*
|
|
* @phan-file-suppress PhanPluginRemoveDebugAny
|
|
*/
|
|
|
|
use Phan\CLI;
|
|
use Phan\CLIBuilder;
|
|
use Phan\Config;
|
|
use Phan\Phan;
|
|
|
|
/**
|
|
* Print usage for phantasm and exit.
|
|
* @return never
|
|
*/
|
|
function phantasm_usage(int $status) : void {
|
|
global $argv;
|
|
$program = $argv[0];
|
|
fwrite($status !== 0 ? STDERR : STDOUT, <<<EOT
|
|
Usage: $program [--in-place|--output-directory <dir>]
|
|
|
|
Options:
|
|
--in-place: Modify files in place. This may result in loss of uncommitted changes.
|
|
--output-directory <dir>: Save new files to a different directory.
|
|
-h, --help: Output this help message.
|
|
|
|
EOT
|
|
);
|
|
exit($status);
|
|
}
|
|
call_user_func(static function () : void {
|
|
global $argv;
|
|
$options = getopt(
|
|
"hop:",
|
|
[
|
|
'help',
|
|
'in-place',
|
|
'progress-bar',
|
|
'output-directory:',
|
|
],
|
|
$optind
|
|
);
|
|
$has_any_option = static function (string ...$arg_names) use ($options) : bool {
|
|
foreach ($arg_names as $arg) {
|
|
if (array_key_exists($arg, $options)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if ($has_any_option('h', 'help')) {
|
|
phantasm_usage(0);
|
|
return;
|
|
}
|
|
$remaining_argv = array_slice($argv, $optind);
|
|
if (count($remaining_argv) !== 0) {
|
|
fwrite(STDERR, "ERROR: unexpected arguments: " . json_encode($remaining_argv) . "\n");
|
|
phantasm_usage(1);
|
|
return;
|
|
}
|
|
|
|
require_once(__DIR__ . '/../src/Phan/Bootstrap.php');
|
|
|
|
$cli_builder = new CLIBuilder();
|
|
if ($has_any_option('p', 'progress-bar')) {
|
|
$cli_builder->setOption('progress-bar');
|
|
}
|
|
$cli_builder->setOption('force-polyfill-parser-with-original-tokens');
|
|
$cli_builder->setOption('plugin', dirname(__DIR__) . '/src/Phan/Plugin/Internal/PhantasmPlugin.php');
|
|
// @phan-suppress-next-line PhanThrowTypeAbsentForCall
|
|
$cli = $cli_builder->build();
|
|
if ($has_any_option('o', 'output-directory')) {
|
|
$dir = $options['output-directory'] ?? $options['o'] ?? null;
|
|
if (!is_string($dir) || $dir === '') {
|
|
CLI::printErrorToStderr("Invalid --output-directory\n");
|
|
phantasm_usage(1);
|
|
return;
|
|
}
|
|
if (isset($options['in-place'])) {
|
|
CLI::printErrorToStderr("Cannot combine --output-directory and --in-place\n");
|
|
phantasm_usage(1);
|
|
}
|
|
$resolved_dir = Config::projectPath($dir);
|
|
if ($resolved_dir === __DIR__ || realpath($resolved_dir) === realpath(__DIR__)) {
|
|
fwrite(STDERR, "--output-directory cannot be the same directory as the project directory. Use --in-place to overwrite a project in place\n");
|
|
phantasm_usage(1);
|
|
}
|
|
fwrite(STDERR, "phantasm: Going to output new contents to '$resolved_dir'\n");
|
|
|
|
$_ENV['PHANTASM_OUTPUT_DIRECTORY'] = $resolved_dir;
|
|
} else if (isset($options['in-place'])) {
|
|
$_ENV['PHANTASM_MODIFY_IN_PLACE'] = '1';
|
|
} else {
|
|
CLI::printErrorToStderr("Must either pass --output-directory <dir> or --in-place\n");
|
|
phantasm_usage(1);
|
|
}
|
|
// Generate codebase info after parsing configs (e.g. included_extension_subset)
|
|
$code_base = require(__DIR__ . '/../src/codebase.php');
|
|
|
|
// @phan-suppress-next-line PhanThrowTypeAbsentForCall
|
|
Phan::analyzeFileList($code_base, /** @return string[] */ static function () use($cli) : array {
|
|
return $cli->getFileList();
|
|
});
|
|
});
|