Initial Drupal 11 with DDEV setup

This commit is contained in:
gluebox
2025-10-08 11:39:17 -04:00
commit 89ef74b305
25344 changed files with 2599172 additions and 0 deletions

View File

@ -0,0 +1,113 @@
<?php
namespace Consolidation\SiteProcess\Util;
use Consolidation\SiteAlias\SiteAliasInterface;
use Symfony\Component\Process\Process;
use Consolidation\SiteProcess\Transport\TransportInterface;
/**
* ArgumentProcessor takes a set of arguments and options from the caller
* and processes them with the provided site alias to produce a final
* executable command that will run either locally or on a remote system,
* as applicable.
*/
class ArgumentProcessor
{
private $short_options = ['vv', 'vvv'];
public function getShortOptions(): array
{
return $this->short_options;
}
public function setShortOptions(array $short_options): void
{
$this->short_options = $short_options;
}
/**
* selectArgs selects the appropriate set of arguments for the command
* to be executed and orders them as needed.
*
* @param SiteAliasInterface $siteAlias Description of
* @param array $args Command and arguments to execute (source)
* @param array $options key / value pair of option and value in include
* in final arguments
* @param array $optionsPassedAsArgs key / value pair of option and value
* to include in final arguments after the '--' argument.
* @return array Command and arguments to execute
*/
public function selectArgs(SiteAliasInterface $siteAlias, $args, $options = [], $optionsPassedAsArgs = [])
{
// Split args into three arrays separated by the `--`
list($leadingArgs, $dashDash, $remaingingArgs) = $this->findArgSeparator($args);
$convertedOptions = $this->convertOptions($options);
$convertedOptionsPassedAsArgs = $this->convertOptions($optionsPassedAsArgs);
// If the caller provided options that should be passed as args, then we
// always need a `--`, whether or not one existed to begin with in $args
if (!empty($convertedOptionsPassedAsArgs)) {
$dashDash = ['--'];
}
// Combine our separated args in the correct order. $dashDash will
// always be `['--']` if $optionsPassedAsArgs or $remaingingArgs are
// not empty, and otherwise will usually be empty.
return array_merge(
$leadingArgs,
$convertedOptions,
$dashDash,
$convertedOptionsPassedAsArgs,
$remaingingArgs
);
}
/**
* findArgSeparator finds the "--" argument in the provided arguments list,
* if present, and returns the arguments in three sets.
*
* @return array of three arrays, leading, "--" and trailing
*/
protected function findArgSeparator($args)
{
$pos = array_search('--', $args);
if ($pos === false) {
return [$args, [], []];
}
return [
array_slice($args, 0, $pos),
['--'],
array_slice($args, $pos + 1),
];
}
/**
* convertOptions takes an associative array of options (key / value) and
* converts it to an array of strings in the form --key=value.
*
* @param array $options in key => value form
* @return array options in --option=value form
*/
protected function convertOptions($options)
{
$result = [];
foreach ($options as $option => $value) {
$dashes = str_repeat('-', $this->dashCount($option));
if ($value === true || $value === null) {
$result[] = $dashes . $option;
} elseif ($value === false) {
// Ignore this option.
} else {
$result[] = "{$dashes}{$option}={$value}";
}
}
return $result;
}
protected function dashCount($name): int
{
return in_array($name, $this->getShortOptions()) ? 1 : 2;
}
}

View File

@ -0,0 +1,145 @@
<?php
namespace Consolidation\SiteProcess\Util;
use Consolidation\SiteAlias\SiteAliasInterface;
use Symfony\Component\Process\Process;
use Consolidation\Config\Util\Interpolator;
use Symfony\Component\Console\Output\OutputInterface;
use Consolidation\SiteProcess\Util\ShellOperatorInterface;
/**
* Escape will shell-escape commandline arguments for different platforms.
*/
class Escape
{
/**
* argsForSite escapes each argument in an array for the given site.
*/
public static function argsForSite(SiteAliasInterface $siteAlias, $args)
{
return array_map(
function ($arg) use ($siteAlias) {
return Escape::forSite($siteAlias, $arg);
},
$args
);
}
/**
* forSite escapes the provided argument for the specified alias record.
*/
public static function forSite(SiteAliasInterface $siteAlias, $arg)
{
return static::shellArg($arg, $siteAlias->os());
}
/**
* shellArg escapes the provided argument for the specified OS
*
* @param string|ShellOperatorInterface $arg The argument to escape
* @param string|null $os The OS to escape for. Optional; defaults to LINUX
*
* @return string The escaped string
*/
public static function shellArg($arg, $os = null)
{
// Short-circuit escaping for simple params (keep stuff readable);
// also skip escaping for shell operators (e.g. &&), which must not
// be escaped.
if (($arg instanceof ShellOperatorInterface) || preg_match('|^[a-zA-Z0-9@=.:/_-]*$|', $arg)) {
return (string) $arg;
}
if (static::isWindows($os)) {
return static::windowsArg($arg);
}
return static::linuxArg($arg);
}
/**
* isWindows determines whether the provided OS is Windows.
*
* @param string|null $os The OS to escape for.
*
* @return boolean
*/
public static function isWindows($os = null)
{
// In most cases, $os will be NULL and PHP_OS will be returned. However,
// if an OS is specified in $os, return that instead.
$os = $os ?: PHP_OS;
return strtoupper(substr($os, 0, 3)) === 'WIN';
}
/**
* linuxArg is the Linux version of escapeshellarg().
*
* This is intended to work the same way that escapeshellarg() does on
* Linux. If we need to escape a string that will be used remotely on
* a Linux system, then we need our own implementation of escapeshellarg,
* because the Windows version behaves differently.
*
* Note that we behave somewhat differently than the built-in escapeshellarg()
* with respect to whitespace replacement in order
*
* @param string $arg The argument to escape
*
* @return string The escaped string
*/
public static function linuxArg($arg)
{
// For single quotes existing in the string, we will "exit"
// single-quote mode, add a \' and then "re-enter"
// single-quote mode. The result of this is that
// 'quote' becomes '\''quote'\''
$arg = preg_replace('/\'/', '\'\\\'\'', $arg);
// Replace "\t", "\n", "\r", "\0", "\x0B" with a whitespace.
// Note that this replacement makes Drush's escapeshellarg work differently
// than the built-in escapeshellarg in PHP on Linux, as these characters
// usually are NOT replaced. However, this was done deliberately to be more
// conservative when running _drush_escapeshellarg_linux on Windows
// (this can happen when generating a command to run on a remote Linux server.)
//
// TODO: Perhaps we should only do this if the local system is Windows?
// n.b. that would be a little more complicated to test.
$arg = str_replace(["\t", "\n", "\r", "\0", "\x0B"], ' ', $arg);
// Add surrounding quotes.
$arg = "'" . $arg . "'";
return $arg;
}
/**
* windowsArg is the Windows version of escapeshellarg().
*
* @param string $arg The argument to escape
*
* @return string The escaped string
*/
public static function windowsArg($arg)
{
if ('' === $arg || null === $arg) {
return '""';
}
if (false !== strpos($arg, "\0")) {
$arg = str_replace("\0", '?', $arg);
}
if (!preg_match('/[\/()%!^"<>&|\s]/', $arg)) {
return $arg;
}
// Double up existing backslashes
$arg = preg_replace('/(\\\\+)$/', '$1$1', $arg);
// Replacing whitespace for good measure (see comment above).
$arg = str_replace(["\t", "\n", "\r", "\0", "\x0B"], ' ', $arg);
$arg = str_replace(['"', '^', '%', '!'], ['""', '"^^"', '"^%"', '"^!"'], $arg);
// Add surrounding quotes.
$arg = '"' . $arg . '"';
return $arg;
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace Consolidation\SiteProcess\Util;
use Symfony\Component\Process\Process;
use Consolidation\Config\Util\Interpolator;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\NullOutput;
/**
* RealtimeOutput can be provided to a process object when you want
* to display the output of the running command as it is being produced.
*/
class RealtimeOutputHandler
{
protected $stdout;
protected $stderr;
protected $stdoutMarker = '';
protected $stderrMarker = '';
/**
* Provide the output streams to use for stdout and stderr
*/
const MARKER_ERR = '> ';
public function __construct(OutputInterface $stdout, OutputInterface $stderr)
{
$this->stdout = $stdout;
$this->stderr = $stderr;
$this->stdoutMarker = '';
$this->stderrMarker = self::MARKER_ERR;
}
/**
* This gives us an opportunity to adapt to the settings of the
* process object (e.g. do we need to do anything differently if
* it is in tty mode, etc.)
*/
public function configure(Process $process)
{
return $this;
}
/**
* setStderrMarker defines the string that should be added at
* the beginning of every line of stderr that is printed.
*/
public function setStderrMarker($marker)
{
$this->stderrMarker = $marker;
return $this;
}
/**
* setStdoutMarker defines the string that should be added at
* the beginning of every line of stdout that is printed.
*/
public function setStdoutMarker($marker)
{
$this->stdoutMarker = $marker;
return $this;
}
/**
* hideStdout overrides whatever was formerly stored in $this->stdout
* with a null output buffer so that none of the standard output data
* is visible.
*/
public function hideStdout()
{
$this->stdout = new NullOutput();
$this->stdoutMarker = '';
return $this;
}
/**
* hideStderr serves the same function as hideStdout, but for the
* standard error stream. Note that it is not useful to unconditionally
* call both hideStdout and hideStderr; if no output is desired, then
* the RealtimeOutputHandler should not be used.
*/
public function hideStderr()
{
$this->stderr = new NullOutput();
$this->stderrMarker = '';
return $this;
}
/**
* If this object is used as a callable, then run 'handleOutput'.
*/
public function __invoke($type, $buffer)
{
$this->handleOutput($type, $buffer);
}
/**
* Helper method when you want real-time output from a Process call.
* @param string $type
* @param string $buffer
*/
public function handleOutput($type, $buffer)
{
if (Process::ERR === $type) {
$this->stderr->write($this->addMarker($buffer, $this->stderrMarker), false, OutputInterface::OUTPUT_RAW);
} else {
$this->stdout->write($this->addMarker($buffer, $this->stdoutMarker), false, OutputInterface::OUTPUT_RAW);
}
}
/**
* Make sure that every line in $buffer begins with a MARKER_ERR.
*/
protected function addMarker($buffer, $marker)
{
// Exit early if there is no marker to add
if (empty($marker)) {
return $buffer;
}
// Add a marker on the beginning of every line.
return $marker . rtrim(implode("\n" . $marker, explode("\n", $buffer)), $marker);
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace Consolidation\SiteProcess\Util;
/**
* Shell::op is a static factory that will create shell operators for use
* in command line arguments list. Shell operators are characters that have
* special meaning to the shell, such as "output redirection". When a shell
* operator object is used, it indicates that this element is intended to
* be used as an operator, and is not simply some other parameter to be escaped.
*/
class Shell implements ShellOperatorInterface
{
protected $value;
public static function op($operator)
{
static::validateOp($operator);
return new self($operator);
}
public static function preEscaped($value)
{
return new self($value);
}
public function __construct($value)
{
$this->value = $value;
}
public function __toString()
{
return $this->value;
}
protected static function validateOp($operator)
{
$valid = [
'&&',
'||',
'|',
'<',
'>',
'>>',
';',
];
if (!in_array($operator, $valid)) {
throw new \Exception($operator . ' is not a valid shell operator.');
}
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Consolidation\SiteProcess\Util;
/**
* ShellOperatorInterface is a marker interface indicating that the object
* represents a shell operator.
*/
interface ShellOperatorInterface
{
}

View File

@ -0,0 +1,27 @@
<?php
namespace Consolidation\SiteProcess\Util;
use Symfony\Component\Process\Process;
/**
* Wrapper for universal support of TTY-related functionality across versions of
* Symfony Process.
*/
class Tty
{
/**
* In Symfony Process 4+, this is simply a wrapper for Process::isTtySupported().
* In lower versions, it mimics the same functionality.
*/
public static function isTtySupported()
{
// Start off by checking STDIN with `posix_isatty`, as that appears to be more reliable
if (function_exists('posix_isatty')) {
return posix_isatty(STDIN);
}
if (method_exists('\Symfony\Component\Process\Process', 'isTtySupported')) {
return Process::isTtySupported();
}
return (bool) @proc_open('echo 1 >/dev/null', array(array('file', '/dev/tty', 'r'), array('file', '/dev/tty', 'w'), array('file', '/dev/tty', 'w')), $pipes);
}
}