1
0
mirror of https://github.com/php/phd.git synced 2026-03-23 22:52:05 +01:00
Files
archived-phd/phpdotnet/phd/Options/Handler.php
2024-10-20 13:34:46 +02:00

623 lines
19 KiB
PHP

<?php
namespace phpdotnet\phd;
class Options_Handler implements Options_Interface
{
public function __construct(
private Config $config,
private Format_Factory $formatFactory
) {}
/**
* @return array<string, string>
*/
public function optionList(): array
{
return [
'format:' => 'f:', // The format to render (xhtml, pdf...)
'noindex' => 'I', // Do not re-index
'forceindex' => 'r', // Force re-indexing under all circumstances
'notoc' => 't', // Do not re-create TOC
'docbook:' => 'd:', // The Docbook XML file to render from (.manual.xml)
'output:' => 'o:', // The output directory
'outputfilename:' => 'F:', // The output filename (only useful for bightml/bigpdf)
'partial:' => 'p:', // The ID to render (optionally ignoring its children)
'skip:' => 's:', // The ID to skip (optionally skipping its children too)
'verbose::' => 'v::', // Adjust the verbosity level
'list' => 'l', // List supported packages/formats
'lang:' => 'L:', // Language hint (used by the CHM)
'color:' => 'c:', // Use color output if possible
'highlighter:' => 'g:', // Class used as source code highlighter
'version' => 'V', // Print out version information
'help' => 'h', // Print out help
'package:' => 'P:', // The package of formats
'css:' => 'C:', // External CSS
'xinclude' => 'x', // Automatically process xinclude directives
'ext:' => 'e:', // The file-format extension to use, including the dot
'saveconfig::' => 'S::', // Save the generated config ?
'quit' => 'Q', // Do not run the render. Use with -S to just save the config.
'memoryindex' => 'M', // Use sqlite in memory rather then file
'packagedir:' => 'k:', // Include path for external packages
];
}
/**
* @return array<string, true>
*/
public function option_M(string $k, mixed $v): array
{
return $this->option_memoryindex($k, $v);
}
/**
* @return array<string, true>
*/
public function option_memoryindex(string $k, mixed $v): array
{
return ['memoryindex' => true];
}
/**
* @return array<string, string|array<string>>
*/
public function option_f(string $k, mixed $v): array
{
if ($k === "f") {
return $this->option_format($k, $v);
}
return $this->option_outputfilename($k, $v);
}
/**
* @return array<string, array<string>>
*/
public function option_format(string $k, mixed $v): array
{
$formats = [];
foreach((array)$v as $val) {
if (!in_array($val, $formats)) {
$formats[] = $val;
}
}
return ['output_format' => $formats];
}
/**
* @return array<string, mixed>
*/
public function option_e(string $k, mixed $v): array
{
return $this->option_ext($k, $v);
}
/**
* @return array<string, mixed>
*/
public function option_ext(string $k, mixed $v): array
{
// `--ext=false`: no extension will be used
// `--ext=true`: use the default extension
// `--ext=".foo"`: use the ".foo" extension
return match (self::boolval($v)) {
false => ['ext' => ''],
true => ['ext' => null],
null => ['ext' => $v]
};
}
/**
* @return array<string, string>
*/
public function option_g(string $k, mixed $v): array
{
return $this->option_highlighter($k, $v);
}
/**
* @return array<string, string>
*/
public function option_highlighter(string $k, mixed $v): array
{
return ['highlighter' => $v];
}
/**
* @return array<string, true>
*/
public function option_i(string $k, mixed $v): array
{
return $this->option_noindex($k, $v);
}
/**
* @return array<string, true>
*/
public function option_noindex(string $k, mixed $v): array
{
return ['no_index' => true];
}
/**
* @return array<string, true>
*/
public function option_r(string $k, mixed $v): array
{
return $this->option_forceindex($k, $v);
}
/**
* @return array<string, true>
*/
public function option_forceindex(string $k, mixed $v): array
{
return ['force_index' => true];
}
/**
* @return array<string, true>
*/
public function option_t(string $k, mixed $v): array
{
return $this->option_notoc($k, $v);
}
/**
* @return array<string, true>
*/
public function option_notoc(string $k, mixed $v): array
{
return ['no_toc' => true];
}
/**
* @return array<string, string>
*/
public function option_d(string $k, mixed $v): array
{
return $this->option_docbook($k, $v);
}
/**
* @return array<string, string>
*/
public function option_docbook(string $k, mixed $v): array
{
if (is_array($v)) {
trigger_error("Can only parse one file at a time", E_USER_ERROR);
}
if (!file_exists($v) || is_dir($v) || !is_readable($v)) {
trigger_error(sprintf("'%s' is not a readable docbook file", $v), E_USER_ERROR);
}
return [
'xml_root' => dirname($v),
'xml_file' => $v,
];
}
/**
* @return array<string, string>
*/
public function option_o(string $k, mixed $v): array
{
return $this->option_output($k, $v);
}
/**
* @return array<string, string>
*/
public function option_output(string $k, mixed $v): array
{
if (is_array($v)) {
trigger_error("Only a single output location can be supplied", E_USER_ERROR);
}
if (!file_exists($v)) {
v("Creating output directory..", VERBOSE_MESSAGES);
if (!mkdir($v, 0777, true)) {
v("Can't create output directory : %s", $v, E_USER_ERROR);
}
v("Output directory created", VERBOSE_MESSAGES);
} elseif (!is_dir($v)) {
v("Output directory is a file?", E_USER_ERROR);
}
if (!is_dir($v) || !is_readable($v)) {
trigger_error(sprintf("'%s' is not a valid directory", $v), E_USER_ERROR);
}
$v = (substr($v, strlen($v) - strlen(DIRECTORY_SEPARATOR)) === DIRECTORY_SEPARATOR) ? $v : ($v . DIRECTORY_SEPARATOR);
return ['output_dir' => $v];
}
/**
* @return array<string, string>
*/
public function option_outputfilename(string $k, mixed $v): array
{
if (is_array($v)) {
trigger_error("Only a single output location can be supplied", E_USER_ERROR);
}
$file = basename($v);
return ['output_filename' => $file];
}
/**
* @return array<string, string>
*/
public function option_p(string $k, mixed $v): array
{
if ($k === "P") {
return $this->option_package($k, $v);
}
return $this->option_partial($k, $v);
}
/**
* @return array<string, array<string>>
*/
public function option_partial(string $k, mixed $v): array
{
$render_ids = $this->config->render_ids();
foreach((array)$v as $val) {
$recursive = true;
if (strpos($val, "=") !== false) {
list($val, $recursive) = explode("=", $val);
if (!is_numeric($recursive) && defined($recursive)) {
$recursive = constant($recursive);
}
$recursive = (bool) $recursive;
}
$render_ids[$val] = $recursive;
}
return ['render_ids' => $render_ids];
}
/**
* @return array<string, array<string>>
*/
public function option_package(string $k, mixed $v): array
{
foreach((array)$v as $package) {
if (!in_array($package, $this->config->getSupportedPackages())) {
$supported = implode(', ', $this->config->getSupportedPackages());
trigger_error("Invalid Package (Tried: '$package' Supported: '$supported')", E_USER_ERROR);
}
}
return ['package' => (array) $v];
}
/**
* @return array<string, string>
*/
public function option_q(string $k, mixed $v): array
{
return $this->option_quit($k, $v);
}
/**
* @return array<string, string>
*/
public function option_quit(string $k, mixed $v): array
{
return ['quit' => true];
}
/**
* @return array<string, bool|array<string>>
*/
public function option_s(string $k, mixed $v): array
{
if ($k === "S") {
return $this->option_saveconfig($k, $v);
}
return $this->option_skip($k, $v);
}
/**
* @return array<string, array<string>>
*/
public function option_skip(string $k, mixed $v): array
{
$skip_ids = $this->config->skip_ids();
foreach((array)$v as $val) {
$recursive = true;
if (strpos($val, "=") !== false) {
list($val, $recursive) = explode("=", $val);
if (!is_numeric($recursive) && defined($recursive)) {
$recursive = constant($recursive);
}
$recursive = (bool) $recursive;
}
$skip_ids[$val] = $recursive;
}
return ['skip_ids' => $skip_ids];
}
/**
* @return array<string, bool>
*/
public function option_saveconfig(string $k, mixed $v): array
{
if (is_array($v)) {
trigger_error(sprintf("You cannot pass %s more than once", $k), E_USER_ERROR);
}
$val = is_bool($v) ? true : self::boolval($v);
if (!is_bool($val)) {
trigger_error("yes/no || on/off || true/false || 1/0 expected", E_USER_ERROR);
}
return ['saveconfig' => $val];
}
/**
* @return array<string, int>
*/
public function option_v(string $k, mixed $v): array
{
if ($k[0] === 'V') {
return $this->option_version($k, $v);
}
return $this->option_verbose($k, $v);
}
/**
* @return array<string, int>
*/
public function option_verbose(string $k, mixed $v): array
{
static $verbose = 0;
foreach((array)$v as $val) {
foreach(explode("|", $val) as $const) {
if (defined($const)) {
$verbose |= (int)constant($const);
} elseif (is_numeric($const)) {
$verbose |= (int)$const;
} elseif (empty($const)) {
$verbose = max($verbose, 1);
$verbose <<= 1;
} else {
trigger_error("Unknown option passed to --$k, '$const'", E_USER_ERROR);
}
}
}
error_reporting($GLOBALS['olderrrep'] | $verbose);
return ['verbose' => $verbose];
}
/**
* @return array<string, string>
*/
public function option_l(string $k, mixed $v): array
{
if ($k === "L") {
return $this->option_lang($k, $v);
}
return $this->option_list($k, $v);
}
public function option_list(string $k, mixed $v): never
{
$packageList = $this->config->getSupportedPackages();
echo "Supported packages:\n";
foreach ($packageList as $package) {
$formats = $this->formatFactory::createFactory($package)->getOutputFormats();
echo "\t" . $package . "\n\t\t" . implode("\n\t\t", $formats) . "\n";
}
exit(0);
}
/**
* @return array<string, string>
*/
public function option_lang(string $k, mixed $v): array
{
return ['language' => $v];
}
/**
* @return array<string, bool|array<string>>
*/
public function option_c(string $k, mixed $v): array
{
if ($k === "C") {
return $this->option_css($k, $v);
}
return $this->option_color($k, $v);
}
/**
* @return array<string, bool>
*/
public function option_color(string $k, mixed $v): array
{
if (is_array($v)) {
trigger_error(sprintf("You cannot pass %s more than once", $k), E_USER_ERROR);
}
$val = self::boolval($v);
if (!is_bool($val)) {
trigger_error("yes/no || on/off || true/false || 1/0 expected", E_USER_ERROR);
}
return ['color_output' => $val];
}
/**
* @return array<string, array<string>>
*/
public function option_css(string $k, mixed $v): array
{
$styles = [];
foreach((array)$v as $val) {
if (!in_array($val, $styles)) {
$styles[] = $val;
}
}
return ['css' => $styles];
}
/**
* @return array<string, array<string>>
*/
public function option_k(string $k, mixed $v): array
{
return $this->option_packagedir($k, $v);
}
/**
* @return array<string, array<string>>
*/
public function option_packagedir(string $k, mixed $v): array
{
$packages = $this->config->package_dirs();
foreach((array)$v as $val) {
if ($path = realpath($val)) {
if (!in_array($path, $packages)) {
$packages[] = $path;
}
} else {
v('Invalid path: %s', $val, E_USER_WARNING);
}
}
return ['package_dirs' => $packages];
}
/**
* @return array<string, true>
*/
public function option_x(string $k, mixed $v): array
{
return $this->option_xinclude($k, true);
}
/**
* @return array<string, true>
*/
public function option_xinclude(string $k, mixed $v): array
{
return ['process_xincludes' => true];
}
/**
* Prints out the current PhD and PHP version.
* Exits directly.
*
* @return never
*/
public function option_version(string $k, mixed $v): never
{
$color = $this->config->phd_info_color();
$output = $this->config->phd_info_output();
fprintf($output, "%s\n", term_color('PhD Version: ' . $this->config::VERSION, $color));
$packageList = $this->config->getSupportedPackages();
foreach ($packageList as $package) {
$version = $this->formatFactory::createFactory($package)->getPackageVersion();
fprintf($output, "\t%s: %s\n", term_color($package, $color), term_color($version, $color));
}
fprintf($output, "%s\n", term_color('PHP Version: ' . phpversion(), $color));
fprintf($output, "%s\n", term_color($this->config->copyright(), $color));
exit(0);
}
public function option_h(string $k, mixed $v): never
{
$this->option_help($k, $v);
}
public function option_help(string $k, mixed $v): never
{
echo "PhD version: " .$this->config::VERSION;
echo "\n" . $this->config->copyright() . "\n
-v
--verbose <int> Adjusts the verbosity level
-f <formatname>
--format <formatname> The build format to use
-P <packagename>
--package <packagename> The package to use
-I
--noindex Do not index before rendering but load from cache
(default: " . ($this->config->noindex() ? 'true' : 'false') . ")
-M
--memoryindex Do not save indexing into a file, store it in memory.
(default: " . ($this->config->memoryindex() ? 'true' : 'false') . ")
-r
--forceindex Force re-indexing under all circumstances
(default: " . ($this->config->forceindex() ? 'true' : 'false') . ")
-t
--notoc Do not rewrite TOC before rendering but load from
cache (default: " . ($this->config->notoc() ? 'true' : 'false') . ")
-d <filename>
--docbook <filename> The Docbook file to render from
-x
--xinclude Process XML Inclusions (XInclude)
(default: " . ($this->config->xinclude() ? 'true' : 'false') . ")
-p <id[=bool]>
--partial <id[=bool]> The ID to render, optionally skipping its children
chunks (default to true; render children)
-s <id[=bool]>
--skip <id[=bool]> The ID to skip, optionally skipping its children
chunks (default to true; skip children)
-l
--list Print out the supported packages and formats
-o <directory>
--output <directory> The output directory (default: " . $this->config->output_dir() . ")
-F filename
--outputfilename filename Filename to use when writing standalone formats
(default: <packagename>-<formatname>.<formatext>)
-L <language>
--lang <language> The language of the source file (used by the CHM
theme). (default: " . $this->config->language() . ")
-c <bool>
--color <bool> Enable color output when output is to a terminal
(default: " . ($this->config->color_output() ? 'true' : 'false') . ")
-C <filename>
--css <filename> Link for an external CSS file.
-g <classname>
--highlighter <classname> Use custom source code highlighting php class
-V
--version Print the PhD version information
-h
--help This help
-e <extension>
--ext <extension> The alternative filename extension to use,
including the dot. Use 'false' for no extension.
-S <bool>
--saveconfig <bool> Save the generated config (default: " . ($this->config->saveconfig() ? 'true' : 'false') . ").
-Q
--quit Don't run the build. Use with --saveconfig to
just save the config (default: " . ($this->config->quit() ? 'true' : 'false') . ").
-k
--packagedir Use an external package directory.
Most options can be passed multiple times for greater effect.
";
exit(0);
}
/**
* Makes one of the following strings into a boolean:
* "on", "off", "yes", "no", "false", "true", "0", "1"
*
* Returns boolean true/false on success, null on failure
*/
private static function boolval(mixed $val): ?bool
{
if (!is_string($val)) {
return null;
}
return match ($val) {
"on", "yes", "true", "1" => true,
"off", "no", "false", "0" => false,
default => null
};
}
}