ProcessStream.php 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Mailer\Transport\Smtp\Stream;
  11. use Symfony\Component\Mailer\Exception\TransportException;
  12. /**
  13. * A stream supporting local processes.
  14. *
  15. * @author Fabien Potencier <fabien@symfony.com>
  16. * @author Chris Corbyn
  17. *
  18. * @internal
  19. */
  20. final class ProcessStream extends AbstractStream
  21. {
  22. private string $command;
  23. private bool $interactive = false;
  24. public function setCommand(string $command): void
  25. {
  26. $this->command = $command;
  27. }
  28. public function setInteractive(bool $interactive)
  29. {
  30. $this->interactive = $interactive;
  31. }
  32. public function initialize(): void
  33. {
  34. $descriptorSpec = [
  35. 0 => ['pipe', 'r'],
  36. 1 => ['pipe', 'w'],
  37. 2 => ['pipe', '\\' === \DIRECTORY_SEPARATOR ? 'a' : 'w'],
  38. ];
  39. $pipes = [];
  40. $this->stream = proc_open($this->command, $descriptorSpec, $pipes);
  41. stream_set_blocking($pipes[2], false);
  42. if ($err = stream_get_contents($pipes[2])) {
  43. throw new TransportException('Process could not be started: '.$err);
  44. }
  45. $this->in = &$pipes[0];
  46. $this->out = &$pipes[1];
  47. $this->err = &$pipes[2];
  48. }
  49. public function terminate(): void
  50. {
  51. if (null !== $this->stream) {
  52. fclose($this->in);
  53. $out = stream_get_contents($this->out);
  54. fclose($this->out);
  55. $err = stream_get_contents($this->err);
  56. fclose($this->err);
  57. if (0 !== $exitCode = proc_close($this->stream)) {
  58. $errorMessage = 'Process failed with exit code '.$exitCode.': '.$out.$err;
  59. }
  60. }
  61. parent::terminate();
  62. if (!$this->interactive && isset($errorMessage)) {
  63. throw new TransportException($errorMessage);
  64. }
  65. }
  66. protected function getReadConnectionDescription(): string
  67. {
  68. return 'process '.$this->command;
  69. }
  70. }