Skip to content

Commit

Permalink
Work around upstream regression in Symfony Process.
Browse files Browse the repository at this point in the history
  • Loading branch information
TravisCarden committed Jun 6, 2024
1 parent 8aee717 commit f0daedb
Show file tree
Hide file tree
Showing 15 changed files with 2,199 additions and 6 deletions.
3 changes: 3 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,7 @@
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint"><exclude-pattern>tests</exclude-pattern></rule>
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"><exclude-pattern>tests</exclude-pattern></rule>

<!-- Exclude temporary Symfony Process fork. -->
<exclude-pattern>src/Internal/SymfonyProcess</exclude-pattern>

</ruleset>
3 changes: 3 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ parameters:
- config/services.yml
- docs/services.yml
- src
excludePaths:
# Exclude temporary Symfony Process fork.
- src/Internal/SymfonyProcess
treatPhpDocTypesAsCertain: false
preconditionSystemHash: 8dfddc6171adcfe004a0bfaea2545f8e
translationSystemHash: dec82389af17442fa61cc1fcc6f89c3e
Expand Down
6 changes: 6 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
<include>
<directory suffix=".php">src</directory>
</include>

<!-- Exclude temporary Symfony Process fork. -->
<exclude>
<directory>src/Internal/SymfonyProcess</directory>
</exclude>

<report>
<clover outputFile="var/phpunit/clover.xml"/>
<html outputDirectory="var/phpunit/html-coverage"/>
Expand Down
3 changes: 3 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
]);

$rectorConfig->skip([
// Exclude temporary Symfony Process fork.
__DIR__ . '/src/Internal/SymfonyProcess',

CatchExceptionNameMatchingTypeRector::class => [__DIR__],
EncapsedStringsToSprintfRector::class => [__DIR__],
FinalizeClassesWithoutChildrenRector::class => [__DIR__], // This is duplicative of PHPCS sniff SlevomatCodingStandard.Classes.RequireAbstractOrFinal.
Expand Down
2 changes: 1 addition & 1 deletion src/Internal/Process/Factory/SymfonyProcessFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
use PhpTuf\ComposerStager\API\Exception\LogicException;
use PhpTuf\ComposerStager\API\Path\Value\PathInterface;
use PhpTuf\ComposerStager\API\Translation\Factory\TranslatableFactoryInterface;
use PhpTuf\ComposerStager\Internal\SymfonyProcess\Value\FixedSymfonyProcess as SymfonyProcess;
use PhpTuf\ComposerStager\Internal\Translation\Factory\TranslatableAwareTrait;
use Symfony\Component\Process\Exception\ExceptionInterface as SymfonyExceptionInterface;
use Symfony\Component\Process\Process as SymfonyProcess;

/**
* @package Process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace PhpTuf\ComposerStager\Internal\Process\Factory;

use PhpTuf\ComposerStager\API\Path\Value\PathInterface;
use Symfony\Component\Process\Process as SymfonyProcess;
use PhpTuf\ComposerStager\Internal\SymfonyProcess\Value\FixedSymfonyProcess as SymfonyProcess;

/**
* Creates Symfony Process objects.
Expand Down
2 changes: 1 addition & 1 deletion src/Internal/Process/Service/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
use PhpTuf\ComposerStager\API\Process\Service\ProcessInterface;
use PhpTuf\ComposerStager\API\Translation\Factory\TranslatableFactoryInterface;
use PhpTuf\ComposerStager\Internal\Process\Factory\SymfonyProcessFactoryInterface;
use PhpTuf\ComposerStager\Internal\SymfonyProcess\Value\FixedSymfonyProcess as SymfonyProcess;
use PhpTuf\ComposerStager\Internal\Translation\Factory\TranslatableAwareTrait;
use Symfony\Component\Process\Process as SymfonyProcess;
use Throwable;

/**
Expand Down
177 changes: 177 additions & 0 deletions src/Internal/SymfonyProcess/Value/AbstractPipes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?php

/*
* This file is a temporary fork of part of the Symfony package to work around a regression.
* @see https://github.com/symfony/symfony/pull/57317
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, see
* https://github.com/symfony/symfony/blob/6.4/LICENSE
*/

namespace PhpTuf\ComposerStager\Internal\SymfonyProcess\Value;

use Symfony\Component\Process\Exception\InvalidArgumentException;

/**
* @author Romain Neutron <[email protected]>
*
* @internal
*/
abstract class AbstractPipes implements PipesInterface
{
public array $pipes = [];

private string $inputBuffer = '';
/** @var resource|string|\Iterator */
private $input;
private bool $blocked = true;
private ?string $lastError = null;

/**
* @param resource|string|\Iterator $input
*/
public function __construct($input)
{
if (\is_resource($input) || $input instanceof \Iterator) {
$this->input = $input;
} else {
$this->inputBuffer = (string) $input;
}
}

public function close(): void
{
foreach ($this->pipes as $pipe) {
if (\is_resource($pipe)) {
fclose($pipe);
}
}
$this->pipes = [];
}

/**
* Returns true if a system call has been interrupted.
*/
protected function hasSystemCallBeenInterrupted(): bool
{
$lastError = $this->lastError;
$this->lastError = null;

// stream_select returns false when the `select` system call is interrupted by an incoming signal
return null !== $lastError && false !== stripos($lastError, 'interrupted system call');
}

/**
* Unblocks streams.
*/
protected function unblock(): void
{
if (!$this->blocked) {
return;
}

foreach ($this->pipes as $pipe) {
stream_set_blocking($pipe, 0);
}
if (\is_resource($this->input)) {
stream_set_blocking($this->input, 0);
}

$this->blocked = false;
}

/**
* Writes input to stdin.
*
* @throws InvalidArgumentException When an input iterator yields a non supported value
*/
protected function write(): ?array
{
if (!isset($this->pipes[0])) {
return null;
}
$input = $this->input;

if ($input instanceof \Iterator) {
if (!$input->valid()) {
$input = null;
} elseif (\is_resource($input = $input->current())) {
stream_set_blocking($input, 0);
} elseif (!isset($this->inputBuffer[0])) {
if (!\is_string($input)) {
if (!\is_scalar($input)) {
throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input)));
}
$input = (string) $input;
}
$this->inputBuffer = $input;
$this->input->next();
$input = null;
} else {
$input = null;
}
}

$r = $e = [];
$w = [$this->pipes[0]];

// let's have a look if something changed in streams
if (false === @stream_select($r, $w, $e, 0, 0)) {
return null;
}

foreach ($w as $stdin) {
if (isset($this->inputBuffer[0])) {
$written = fwrite($stdin, $this->inputBuffer);
$this->inputBuffer = substr($this->inputBuffer, $written);
if (isset($this->inputBuffer[0])) {
return [$this->pipes[0]];
}
}

if ($input) {
while (true) {
$data = fread($input, self::CHUNK_SIZE);
if (!isset($data[0])) {
break;
}
$written = fwrite($stdin, $data);
$data = substr($data, $written);
if (isset($data[0])) {
$this->inputBuffer = $data;

return [$this->pipes[0]];
}
}
if (feof($input)) {
if ($this->input instanceof \Iterator) {
$this->input->next();
} else {
$this->input = null;
}
}
}
}

// no input to read on resource, buffer is empty
if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
$this->input = null;
fclose($this->pipes[0]);
unset($this->pipes[0]);
} elseif (!$w) {
return [$this->pipes[0]];
}

return null;
}

/**
* @internal
*/
public function handleError(int $type, string $msg): void
{
$this->lastError = $msg;
}
}
Loading

0 comments on commit f0daedb

Please sign in to comment.