Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM php:8.4-cli

ARG UID=1000
ARG GID=1000

RUN apt-get update && \
apt-get install -y git default-jre-headless

WORKDIR /var/www

ADD https://api.github.com/repos/php/phd/git/refs/heads/master version-phd.json
ADD https://api.github.com/repos/php/docbook-cs/git/refs/heads/main version-docbook-cs.json

RUN echo 'memory_limit = 512M' >> /usr/local/etc/php/conf.d/local.ini

RUN chown $UID:$GID /var/www

USER $UID:$GID

RUN git clone --depth 1 https://github.com/php/phd.git && \
git clone --depth 1 https://github.com/php/docbook-cs.git
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ fileModHistory.php

# A plece for all temporary or generated files (idempotent build)
temp/

# Docker dev build stamp
.docker/built
17 changes: 17 additions & 0 deletions dev.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php

/**
* Dev build tool for the PHP manual, usable for every language
* Run "php dev.php help" for usage
*/

declare(strict_types=1);

spl_autoload_register(static function (string $class): void {
if (str_starts_with($class, 'PhpDoc\\Dev\\')) {
require __DIR__ . '/scripts/dev/' . str_replace('\\', '/', substr($class, 11)) . '.php';
}
});

exit((new PhpDoc\Dev\Application(__DIR__))->run(array_slice($argv, 1)));
44 changes: 44 additions & 0 deletions docbookcs.dev.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Fallback docbook-cs configuration for translations that do not ship their
own docbookcs.xml. @LANG@ is replaced with the target language.
-->
<docbookcs xmlns="https://php.github.io/docbook-cs/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://php.github.io/docbook-cs/config
https://php.github.io/docbook-cs/config.xsd">

<project>
<directory alias="doc-@LANG@">@LANG@</directory>
<directory>doc-base</directory>
</project>

<sniffs>
<sniff class="DocbookCS\Sniff\SimparaSniff" />
<sniff class="DocbookCS\Sniff\ExceptionNameSniff" />
<sniff class="DocbookCS\Sniff\AttributeOrderSniff" />
<sniff class="DocbookCS\Sniff\MixedIndentationSniff" />
<sniff class="DocbookCS\Sniff\TrailingWhitespaceSniff" />
</sniffs>

<paths>
<path>.</path>
</paths>

<entities>
<file>extensions.ent</file>
<file>language-defs.ent</file>
<file>language-snippets.ent</file>
<directory>entities/</directory>
<directory>../doc-base/entities/</directory>
<file>../doc-base/temp/manual.ent</file>
<file>../doc-base/temp/entities.ent</file>
<file>../doc-base/temp/file-entities.ent</file>
<directory>../doc-base/temp/file-entities</directory>
</entities>

<exclude>
<pattern>output/*</pattern>
</exclude>

</docbookcs>
182 changes: 182 additions & 0 deletions scripts/dev/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?php

declare(strict_types=1);

namespace PhpDoc\Dev;

use PhpDoc\Dev\Command\BuildCommand;
use PhpDoc\Dev\Command\ConfigureCommand;
use PhpDoc\Dev\Command\HelpCommand;
use PhpDoc\Dev\Command\LintCommand;
use PhpDoc\Dev\Command\PullCommand;
use PhpDoc\Dev\Command\RenderCommand;
use PhpDoc\Dev\Command\ServeCommand;
use PhpDoc\Dev\Command\ShellCommand;
use PhpDoc\Dev\Environment\DockerEnvironment;
use PhpDoc\Dev\Environment\LocalEnvironment;

final class Application
{
private const MINIMUM_PHP_VERSION = 80_400;

public function __construct(private readonly string $basedir)
{
}

/**
* @param list<string> $args Command line arguments, without argv[0].
*/
public function run(array $args): int
{
if (!$this->requirePhpVersion(self::MINIMUM_PHP_VERSION)) {
return 1;
}

$options = new Options();
$command = $this->parse($args, $options);

if ($command === null) {
return 1;
}

if ($command === 'help') {
return (new HelpCommand())->execute($options);
}

$subcommand = null;

if ($command === 'docker') {
$subcommand = array_shift($options->args);

if (!in_array($subcommand, ['build', 'shell'], true)) {
fwrite(STDERR, "Usage: php dev.php docker <build|shell> (see: php dev.php help)\n");
return 1;
}

$options->docker = true;
}

if ($command === 'render') {
$subcommand = array_shift($options->args);

if (!in_array($subcommand, ['xhtml', 'php'], true)) {
fwrite(STDERR, "Usage: php dev.php render <xhtml|php> (see: php dev.php help)\n");
return 1;
}

$options->format = $subcommand;
}

if ($command === 'cs') {
$subcommand = array_shift($options->args);

if (!in_array($subcommand, ['lint', 'fix'], true)) {
fwrite(STDERR, "Usage: php dev.php cs <lint|fix> (see: php dev.php help)\n");
return 1;
}
}

$runner = new ProcessRunner();
$dockerAvailable = $runner->runQuiet(['docker', 'version', '--format', '{{.Server.Version}}']) === 0;

if ($options->docker === true && !$dockerAvailable) {
fwrite(STDERR, "error: Docker requested but the docker command is not available.\n");
return 1;
}

$workspace = new Workspace($this->basedir, $runner, $options->assumeYes);
$environment = ($options->docker ?? $dockerAvailable)
? new DockerEnvironment($workspace, $runner)
: new LocalEnvironment($workspace, $runner);

$configure = new ConfigureCommand($workspace, $environment);

switch ($command) {
case 'pull':
return (new PullCommand($workspace, $environment))->execute($options);
case 'configure':
return $configure->execute($options);
case 'render':
return (new RenderCommand($workspace, $environment, $configure))->execute($options);
case 'cs':
return (new LintCommand($workspace, $environment, $configure, fix: $subcommand === 'fix'))
->execute($options);
case 'serve':
return (new ServeCommand($workspace, $environment))->execute($options);
case 'docker':
return $subcommand === 'build'
? (new BuildCommand($environment))->execute($options)
: (new ShellCommand($environment))->execute($options);
}

fwrite(STDERR, "Unknown command: $command (see: php dev.php help)\n");

return 1;
}

/** @param list<string> $args */
private function parse(array $args, Options $options): ?string
{
$command = null;

foreach ($args as $arg) {
if (preg_match('/^--lang=(.+)$/', $arg, $m)) {
$options->lang = $m[1];
continue;
}

if (preg_match('/^--port=(\d+)$/', $arg, $m)) {
$options->port = (int) $m[1];
continue;
}

if ($arg === '--docker') {
$options->docker = true;
continue;
}

if ($arg === '--no-docker') {
$options->docker = false;
continue;
}

if ($arg === '--yes' || $arg === '-y') {
$options->assumeYes = true;
continue;
}

if ($command === null) {
if ($arg[0] !== '-') {
$command = $arg;
continue;
}

if ($arg === '-h' || $arg === '--help') {
$command = 'help';
continue;
}

$fileName = $_SERVER['SCRIPT_FILENAME'];
fwrite(STDERR, "Unknown option: $arg (see: php $fileName help)\n");
return null;
}

$options->args[] = $arg;
}

return $command ?? 'help';
}

private function requirePhpVersion(int $minimum): bool
{
if (PHP_VERSION_ID >= $minimum) {
return true;
}

$need = sprintf('%d.%d', intdiv($minimum, 10000), intdiv($minimum % 10000, 100));
fwrite(STDERR, "error: dev.php requires PHP $need+ without Docker (this is PHP "
. PHP_VERSION . "). Install Docker or a newer PHP.\n");

return false;
}
}
20 changes: 20 additions & 0 deletions scripts/dev/Command/BuildCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace PhpDoc\Dev\Command;

use PhpDoc\Dev\Environment\Environment;
use PhpDoc\Dev\Options;

final class BuildCommand implements Command
{
public function __construct(private readonly Environment $environment)
{
}

public function execute(Options $options): int
{
return $this->environment->buildImage();
}
}
12 changes: 12 additions & 0 deletions scripts/dev/Command/Command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace PhpDoc\Dev\Command;

use PhpDoc\Dev\Options;

interface Command
{
public function execute(Options $options): int;
}
38 changes: 38 additions & 0 deletions scripts/dev/Command/ConfigureCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace PhpDoc\Dev\Command;

use PhpDoc\Dev\Environment\Environment;
use PhpDoc\Dev\Options;
use PhpDoc\Dev\Workspace;

final class ConfigureCommand implements Command
{
public function __construct(
private readonly Workspace $workspace,
private readonly Environment $environment,
) {
}

public function execute(Options $options): int
{
$lang = $options->lang;

if (!$this->workspace->ensureLangRepos($lang, $this->environment->canMapDirectoryNames())) {
return 1;
}

$this->workspace->pullSideRepos($lang);

$args = array_merge([
($this->workspace->isBaseLang($lang) ? '--with-base-lang=' : '--with-lang=') . $lang,
'--enable-xml-details',
'--disable-libxml-check',
'--redirect-stderr-to-stdout',
], $options->args);

return $this->environment->configure($lang, $args);
}
}
50 changes: 50 additions & 0 deletions scripts/dev/Command/HelpCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace PhpDoc\Dev\Command;

use PhpDoc\Dev\Options;

final class HelpCommand implements Command
{
public function execute(Options $options): int
{
echo <<<HELP
Dev build tool for the PHP manual, for every language, with or without
Docker. Missing sibling repositories (en, the translation, and without
Docker also phd/docbook-cs) are cloned automatically on first use.
With Docker, phd and docbook-cs ship inside the image and follow their
upstream heads automatically; a checkout next to doc-base overrides
the image's copy and is only moved by pull.

Usage:
php dev.php <command> [options] [extra arguments]

Commands:
pull Clone missing sibling repositories and update existing ones
configure Assemble and validate the manual, without rendering
render xhtml configure + render the chunked XHTML manual to <lang>/output
render php configure + render the web (PHP) version to <lang>/output
cs lint Run docbook-cs; extra arguments are passed through (paths, --wide)
cs fix Same as cs lint, with --fix: rewrite violations that have fixers
serve Serve <lang>/output over HTTP
docker build Build the Docker image
docker shell Interactive shell inside the container

Options:
--lang=XX Language to operate on (default: en)
--port=NNNN Port for serve (default: 8080)
--docker Force Docker mode (default: used when available)
--no-docker Force local mode
--yes, -y Clone missing repositories without asking for confirmation

Any other argument after the command is passed through: to configure.php
for configure/render (e.g. --enable-xml-details), and to
docbook-cs for cs lint/cs fix (e.g. reference/datetime --wide).

HELP;

return 0;
}
}
Loading