Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\InMemory;
use Psr\Clock\ClockInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\LogicException;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
use Symfony\Contracts\Service\ResetInterface;
/**
* Transport that stays in memory. Useful for testing purpose.
*
* @author Gary PEGEOT <garypegeot@gmail.com>
*/
class InMemoryTransport implements TransportInterface, ResetInterface
{
/**
* @var Envelope[]
*/
private array $sent = [];
/**
* @var Envelope[]
*/
private array $acknowledged = [];
/**
* @var Envelope[]
*/
private array $rejected = [];
/**
* @var Envelope[]
*/
private array $queue = [];
private int $nextId = 1;
private array $availableAt = [];
public function __construct(
private ?SerializerInterface $serializer = null,
private ?ClockInterface $clock = null,
) {
}
public function get(): iterable
{
$envelopes = [];
$now = $this->clock?->now() ?? new \DateTimeImmutable();
foreach ($this->decode($this->queue) as $id => $envelope) {
if (!isset($this->availableAt[$id]) || $now > $this->availableAt[$id]) {
$envelopes[] = $envelope;
}
}
return $envelopes;
}
public function ack(Envelope $envelope): void
{
$this->acknowledged[] = $this->encode($envelope);
if (!$transportMessageIdStamp = $envelope->last(TransportMessageIdStamp::class)) {
throw new LogicException('No TransportMessageIdStamp found on the Envelope.');
}
unset($this->queue[$id = $transportMessageIdStamp->getId()], $this->availableAt[$id]);
}
public function reject(Envelope $envelope): void
{
$this->rejected[] = $this->encode($envelope);
if (!$transportMessageIdStamp = $envelope->last(TransportMessageIdStamp::class)) {
throw new LogicException('No TransportMessageIdStamp found on the Envelope.');
}
unset($this->queue[$id = $transportMessageIdStamp->getId()], $this->availableAt[$id]);
}
public function send(Envelope $envelope): Envelope
{
$id = $this->nextId++;
$envelope = $envelope->with(new TransportMessageIdStamp($id));
$encodedEnvelope = $this->encode($envelope);
$this->sent[] = $encodedEnvelope;
$this->queue[$id] = $encodedEnvelope;
/** @var DelayStamp|null $delayStamp */
if ($delayStamp = $envelope->last(DelayStamp::class)) {
$now = $this->clock?->now() ?? new \DateTimeImmutable();
$this->availableAt[$id] = $now->modify(\sprintf('+%d seconds', $delayStamp->getDelay() / 1000));
}
return $envelope;
}
/**
* @return void
*/
public function reset()
{
$this->sent = $this->queue = $this->rejected = $this->acknowledged = [];
}
/**
* @return Envelope[]
*/
public function getAcknowledged(): array
{
return $this->decode($this->acknowledged);
}
/**
* @return Envelope[]
*/
public function getRejected(): array
{
return $this->decode($this->rejected);
}
/**
* @return Envelope[]
*/
public function getSent(): array
{
return $this->decode($this->sent);
}
private function encode(Envelope $envelope): Envelope|array
{
if (null === $this->serializer) {
return $envelope;
}
return $this->serializer->encode($envelope);
}
/**
* @param array<mixed> $messagesEncoded
*
* @return Envelope[]
*/
private function decode(array $messagesEncoded): array
{
if (null === $this->serializer) {
return $messagesEncoded;
}
return array_map($this->serializer->decode(...), $messagesEncoded);
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\InMemory;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
use Symfony\Contracts\Service\ResetInterface;
/**
* @author Gary PEGEOT <garypegeot@gmail.com>
*
* @implements TransportFactoryInterface<InMemoryTransport>
*/
class InMemoryTransportFactory implements TransportFactoryInterface, ResetInterface
{
/**
* @var InMemoryTransport[]
*/
private array $createdTransports = [];
public function createTransport(string $dsn, array $options, SerializerInterface $serializer): TransportInterface
{
['serialize' => $serialize] = $this->parseDsn($dsn);
return $this->createdTransports[] = new InMemoryTransport($serialize ? $serializer : null);
}
public function supports(string $dsn, array $options): bool
{
return str_starts_with($dsn, 'in-memory://');
}
/**
* @return void
*/
public function reset()
{
foreach ($this->createdTransports as $transport) {
$transport->reset();
}
}
private function parseDsn(string $dsn): array
{
$query = [];
if ($queryAsString = strstr($dsn, '?')) {
parse_str(ltrim($queryAsString, '?'), $query);
}
return [
'serialize' => filter_var($query['serialize'] ?? false, \FILTER_VALIDATE_BOOL),
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport as BaseInMemoryTransport;
trigger_deprecation('symfony/messenger', '6.3', 'The "%s" class is deprecated, use "%s" instead. ', InMemoryTransport::class, BaseInMemoryTransport::class);
/**
* @deprecated since Symfony 6.3, use {@link BaseInMemoryTransport} instead
*/
class InMemoryTransport extends BaseInMemoryTransport
{
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Transport\InMemory\InMemoryTransportFactory as BaseInMemoryTransportFactory;
trigger_deprecation('symfony/messenger', '6.3', 'The "%s" class is deprecated, use "%s" instead. ', InMemoryTransportFactory::class, BaseInMemoryTransportFactory::class);
/**
* @deprecated since Symfony 6.3, use {@link BaseInMemoryTransportFactory} instead
*/
class InMemoryTransportFactory extends BaseInMemoryTransportFactory
{
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Receiver;
use Symfony\Component\Messenger\Envelope;
/**
* Used when a receiver has the ability to list messages and find specific messages.
* A receiver that implements this should add the TransportMessageIdStamp
* to the Envelopes that it returns.
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
interface ListableReceiverInterface extends ReceiverInterface
{
/**
* Returns all the messages (up to the limit) in this receiver.
*
* Messages should be given the same stamps as when using ReceiverInterface::get().
*
* @return Envelope[]|iterable
*/
public function all(?int $limit = null): iterable;
/**
* Returns the Envelope by id or none.
*
* Message should be given the same stamps as when using ReceiverInterface::get().
*/
public function find(mixed $id): ?Envelope;
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Receiver;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
interface MessageCountAwareInterface
{
/**
* Returns the number of messages waiting to be handled.
*
* In some systems, this may be an approximate number.
*/
public function getMessageCount(): int;
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Receiver;
use Symfony\Component\Messenger\Envelope;
/**
* Some transports may have multiple queues. This interface is used to read from only some queues.
*
* @author David Buchmann <mail@davidbu.ch>
*/
interface QueueReceiverInterface extends ReceiverInterface
{
/**
* Get messages from the specified queue names instead of consuming from all queues.
*
* @param string[] $queueNames
*
* @return Envelope[]
*/
public function getFromQueues(array $queueNames): iterable;
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Receiver;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\TransportException;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
interface ReceiverInterface
{
/**
* Receives some messages.
*
* While this method could return an unlimited number of messages,
* the intention is that it returns only one, or a "small number"
* of messages each time. This gives the user more flexibility:
* they can finish processing the one (or "small number") of messages
* from this receiver and move on to check other receivers for messages.
* If this method returns too many messages, it could cause a
* blocking effect where handling the messages received from one
* call to get() takes a long time, blocking other receivers from
* being called.
*
* If applicable, the Envelope should contain a TransportMessageIdStamp.
*
* If a received message cannot be decoded, the message should not
* be retried again (e.g. if there's a queue, it should be removed)
* and a MessageDecodingFailedException should be thrown.
*
* @return iterable<Envelope>
*
* @throws TransportException If there is an issue communicating with the transport
*/
public function get(): iterable;
/**
* Acknowledges that the passed message was handled.
*
* @throws TransportException If there is an issue communicating with the transport
*/
public function ack(Envelope $envelope): void;
/**
* Called when handling the message failed and it should not be retried.
*
* @throws TransportException If there is an issue communicating with the transport
*/
public function reject(Envelope $envelope): void;
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Receiver;
use Symfony\Component\Messenger\Envelope;
/**
* Receiver that decorates another, but receives only 1 specific message.
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*
* @internal
*/
class SingleMessageReceiver implements ReceiverInterface
{
private ReceiverInterface $receiver;
private Envelope $envelope;
private bool $hasReceived = false;
public function __construct(ReceiverInterface $receiver, Envelope $envelope)
{
$this->receiver = $receiver;
$this->envelope = $envelope;
}
public function get(): iterable
{
if ($this->hasReceived) {
return [];
}
$this->hasReceived = true;
return [$this->envelope];
}
public function ack(Envelope $envelope): void
{
$this->receiver->ack($envelope);
}
public function reject(Envelope $envelope): void
{
$this->receiver->reject($envelope);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Sender;
use Symfony\Component\Messenger\Envelope;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
*/
interface SenderInterface
{
/**
* Sends the given envelope.
*
* The sender can read different stamps for transport configuration,
* like delivery delay.
*
* If applicable, the returned Envelope should contain a TransportMessageIdStamp.
*/
public function send(Envelope $envelope): Envelope;
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Sender;
use Psr\Container\ContainerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\RuntimeException;
use Symfony\Component\Messenger\Handler\HandlersLocator;
use Symfony\Component\Messenger\Stamp\TransportNamesStamp;
/**
* Maps a message to a list of senders.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SendersLocator implements SendersLocatorInterface
{
private array $sendersMap;
private ContainerInterface $sendersLocator;
/**
* @param array<string, list<string>> $sendersMap An array, keyed by "type", set to an array of sender aliases
* @param ContainerInterface $sendersLocator Locator of senders, keyed by sender alias
*/
public function __construct(array $sendersMap, ContainerInterface $sendersLocator)
{
$this->sendersMap = $sendersMap;
$this->sendersLocator = $sendersLocator;
}
public function getSenders(Envelope $envelope): iterable
{
if ($envelope->all(TransportNamesStamp::class)) {
foreach ($envelope->last(TransportNamesStamp::class)->getTransportNames() as $senderAlias) {
yield from $this->getSenderFromAlias($senderAlias);
}
return;
}
$seen = [];
foreach (HandlersLocator::listTypes($envelope) as $type) {
if (str_ends_with($type, '*') && $seen) {
// the '*' acts as a fallback, if other senders already matched
// with previous types, skip the senders bound to the fallback
continue;
}
foreach ($this->sendersMap[$type] ?? [] as $senderAlias) {
if (!\in_array($senderAlias, $seen, true)) {
$seen[] = $senderAlias;
yield from $this->getSenderFromAlias($senderAlias);
}
}
}
}
private function getSenderFromAlias(string $senderAlias): iterable
{
if (!$this->sendersLocator->has($senderAlias)) {
throw new RuntimeException(\sprintf('Invalid senders configuration: sender "%s" is not in the senders locator.', $senderAlias));
}
yield $senderAlias => $this->sendersLocator->get($senderAlias);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Sender;
use Symfony\Component\Messenger\Envelope;
/**
* Maps a message to a list of senders.
*
* @author Samuel Roze <samuel.roze@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
interface SendersLocatorInterface
{
/**
* Gets the senders for the given message name.
*
* @return iterable<string, SenderInterface> Indexed by sender alias if available
*/
public function getSenders(Envelope $envelope): iterable;
}

View File

@@ -0,0 +1,90 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Serialization\Normalizer;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\Messenger\Transport\Serialization\Serializer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* This normalizer is only used in Debug/Dev/Messenger contexts.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
final class FlattenExceptionNormalizer implements DenormalizerInterface, NormalizerInterface
{
use NormalizerAwareTrait;
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
$normalized = [
'message' => $object->getMessage(),
'code' => $object->getCode(),
'headers' => $object->getHeaders(),
'class' => $object->getClass(),
'file' => $object->getFile(),
'line' => $object->getLine(),
'previous' => null === $object->getPrevious() ? null : $this->normalize($object->getPrevious(), $format, $context),
'status' => $object->getStatusCode(),
'status_text' => $object->getStatusText(),
'trace' => $object->getTrace(),
'trace_as_string' => $object->getTraceAsString(),
];
return $normalized;
}
public function getSupportedTypes(?string $format): array
{
return [
FlattenException::class => false,
];
}
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
{
return $data instanceof FlattenException && ($context[Serializer::MESSENGER_SERIALIZATION_CONTEXT] ?? false);
}
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): FlattenException
{
$object = new FlattenException();
$object->setMessage($data['message']);
$object->setCode($data['code']);
$object->setStatusCode($data['status'] ?? 500);
$object->setClass($data['class']);
$object->setFile($data['file']);
$object->setLine($data['line']);
$object->setStatusText($data['status_text']);
$object->setHeaders((array) $data['headers']);
if (isset($data['previous'])) {
$object->setPrevious($this->denormalize($data['previous'], $type, $format, $context));
}
$property = new \ReflectionProperty(FlattenException::class, 'trace');
$property->setValue($object, (array) $data['trace']);
$property = new \ReflectionProperty(FlattenException::class, 'traceAsString');
$property->setValue($object, $data['trace_as_string']);
return $object;
}
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
{
return FlattenException::class === $type && ($context[Serializer::MESSENGER_SERIALIZATION_CONTEXT] ?? false);
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Serialization;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
use Symfony\Component\Messenger\Stamp\MessageDecodingFailedStamp;
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
/**
* @author Ryan Weaver<ryan@symfonycasts.com>
*/
class PhpSerializer implements SerializerInterface
{
private bool $acceptPhpIncompleteClass = false;
/**
* @internal
*/
public function acceptPhpIncompleteClass(): void
{
$this->acceptPhpIncompleteClass = true;
}
/**
* @internal
*/
public function rejectPhpIncompleteClass(): void
{
$this->acceptPhpIncompleteClass = false;
}
public function decode(array $encodedEnvelope): Envelope
{
if (empty($encodedEnvelope['body'])) {
throw new MessageDecodingFailedException('Encoded envelope should have at least a "body", or maybe you should implement your own serializer.');
}
if (!str_ends_with($encodedEnvelope['body'], '}')) {
$encodedEnvelope['body'] = base64_decode($encodedEnvelope['body']);
}
$serializeEnvelope = stripslashes($encodedEnvelope['body']);
return $this->safelyUnserialize($serializeEnvelope);
}
public function encode(Envelope $envelope): array
{
$envelope = $envelope->withoutStampsOfType(NonSendableStampInterface::class);
$body = addslashes(serialize($envelope));
if (!preg_match('//u', $body)) {
$body = base64_encode($body);
}
return [
'body' => $body,
];
}
private function safelyUnserialize(string $contents): Envelope
{
if ('' === $contents) {
throw new MessageDecodingFailedException('Could not decode an empty message using PHP serialization.');
}
if ($this->acceptPhpIncompleteClass) {
$prevUnserializeHandler = ini_set('unserialize_callback_func', null);
} else {
$prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback');
}
$prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler) {
if (__FILE__ === $file && !\in_array($type, [\E_DEPRECATED, \E_USER_DEPRECATED], true)) {
throw new \ErrorException($msg, 0, $type, $file, $line);
}
return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
});
try {
/** @var Envelope */
$envelope = unserialize($contents);
} catch (\Throwable $e) {
if ($e instanceof MessageDecodingFailedException) {
throw $e;
}
throw new MessageDecodingFailedException('Could not decode Envelope: '.$e->getMessage(), 0, $e);
} finally {
restore_error_handler();
ini_set('unserialize_callback_func', $prevUnserializeHandler);
}
if (!$envelope instanceof Envelope) {
throw new MessageDecodingFailedException('Could not decode message into an Envelope.');
}
if ($envelope->getMessage() instanceof \__PHP_Incomplete_Class) {
$envelope = $envelope->with(new MessageDecodingFailedStamp());
}
return $envelope;
}
/**
* @internal
*/
public static function handleUnserializeCallback(string $class): never
{
throw new MessageDecodingFailedException(\sprintf('Message class "%s" not found during decoding.', $class));
}
}

View File

@@ -0,0 +1,181 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Serialization;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\LogicException;
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
use Symfony\Component\Messenger\Stamp\SerializedMessageStamp;
use Symfony\Component\Messenger\Stamp\SerializerStamp;
use Symfony\Component\Messenger\Stamp\StampInterface;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer as SymfonySerializer;
use Symfony\Component\Serializer\SerializerInterface as SymfonySerializerInterface;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
*/
class Serializer implements SerializerInterface
{
public const MESSENGER_SERIALIZATION_CONTEXT = 'messenger_serialization';
private const STAMP_HEADER_PREFIX = 'X-Message-Stamp-';
private SymfonySerializerInterface $serializer;
private string $format;
private array $context;
public function __construct(?SymfonySerializerInterface $serializer = null, string $format = 'json', array $context = [])
{
$this->serializer = $serializer ?? self::create()->serializer;
$this->format = $format;
$this->context = $context + [self::MESSENGER_SERIALIZATION_CONTEXT => true];
}
public static function create(): self
{
if (!class_exists(SymfonySerializer::class)) {
throw new LogicException(\sprintf('The "%s" class requires Symfony\'s Serializer component. Try running "composer require symfony/serializer" or use "%s" instead.', __CLASS__, PhpSerializer::class));
}
$encoders = [new XmlEncoder(), new JsonEncoder()];
$normalizers = [new DateTimeNormalizer(), new ArrayDenormalizer(), new ObjectNormalizer()];
$serializer = new SymfonySerializer($normalizers, $encoders);
return new self($serializer);
}
public function decode(array $encodedEnvelope): Envelope
{
if (empty($encodedEnvelope['body']) || empty($encodedEnvelope['headers'])) {
throw new MessageDecodingFailedException('Encoded envelope should have at least a "body" and some "headers", or maybe you should implement your own serializer.');
}
if (empty($encodedEnvelope['headers']['type'])) {
throw new MessageDecodingFailedException('Encoded envelope does not have a "type" header.');
}
$stamps = $this->decodeStamps($encodedEnvelope);
$stamps[] = new SerializedMessageStamp($encodedEnvelope['body']);
$serializerStamp = $this->findFirstSerializerStamp($stamps);
$context = $this->context;
if (null !== $serializerStamp) {
$context = $serializerStamp->getContext() + $context;
}
try {
$message = $this->serializer->deserialize($encodedEnvelope['body'], $encodedEnvelope['headers']['type'], $this->format, $context);
} catch (ExceptionInterface $e) {
throw new MessageDecodingFailedException('Could not decode message: '.$e->getMessage(), $e->getCode(), $e);
}
return new Envelope($message, $stamps);
}
public function encode(Envelope $envelope): array
{
$context = $this->context;
/** @var SerializerStamp|null $serializerStamp */
if ($serializerStamp = $envelope->last(SerializerStamp::class)) {
$context = $serializerStamp->getContext() + $context;
}
/** @var SerializedMessageStamp|null $serializedMessageStamp */
$serializedMessageStamp = $envelope->last(SerializedMessageStamp::class);
$envelope = $envelope->withoutStampsOfType(NonSendableStampInterface::class);
$headers = ['type' => $envelope->getMessage()::class] + $this->encodeStamps($envelope) + $this->getContentTypeHeader();
return [
'body' => $serializedMessageStamp
? $serializedMessageStamp->getSerializedMessage()
: $this->serializer->serialize($envelope->getMessage(), $this->format, $context),
'headers' => $headers,
];
}
private function decodeStamps(array $encodedEnvelope): array
{
$stamps = [];
foreach ($encodedEnvelope['headers'] as $name => $value) {
if (!str_starts_with($name, self::STAMP_HEADER_PREFIX)) {
continue;
}
try {
$stamps[] = $this->serializer->deserialize($value, substr($name, \strlen(self::STAMP_HEADER_PREFIX)).'[]', $this->format, $this->context);
} catch (ExceptionInterface $e) {
throw new MessageDecodingFailedException('Could not decode stamp: '.$e->getMessage(), $e->getCode(), $e);
}
}
if ($stamps) {
$stamps = array_merge(...$stamps);
}
return $stamps;
}
private function encodeStamps(Envelope $envelope): array
{
if (!$allStamps = $envelope->all()) {
return [];
}
$headers = [];
foreach ($allStamps as $class => $stamps) {
$headers[self::STAMP_HEADER_PREFIX.$class] = $this->serializer->serialize($stamps, $this->format, $this->context);
}
return $headers;
}
/**
* @param StampInterface[] $stamps
*/
private function findFirstSerializerStamp(array $stamps): ?SerializerStamp
{
foreach ($stamps as $stamp) {
if ($stamp instanceof SerializerStamp) {
return $stamp;
}
}
return null;
}
private function getContentTypeHeader(): array
{
$mimeType = $this->getMimeTypeForFormat();
return null === $mimeType ? [] : ['Content-Type' => $mimeType];
}
private function getMimeTypeForFormat(): ?string
{
return match ($this->format) {
'json' => 'application/json',
'xml' => 'application/xml',
'yml',
'yaml' => 'application/x-yaml',
'csv' => 'text/csv',
default => null,
};
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Serialization;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\MessageDecodingFailedException;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
*/
interface SerializerInterface
{
/**
* Decodes an envelope and its message from an encoded-form.
*
* The `$encodedEnvelope` parameter is a key-value array that
* describes the envelope and its content, that will be used by the different transports.
*
* The most common keys are:
* - `body` (string) - the message body
* - `headers` (string<string>) - a key/value pair of headers
*
* @throws MessageDecodingFailedException
*/
public function decode(array $encodedEnvelope): Envelope;
/**
* Encodes an envelope content (message & stamps) to a common format understandable by transports.
* The encoded array should only contain scalars and arrays.
*
* Stamps that implement NonSendableStampInterface should
* not be encoded.
*
* The most common keys of the encoded array are:
* - `body` (string) - the message body
* - `headers` (string<string>) - a key/value pair of headers
*/
public function encode(Envelope $envelope): array;
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport;
/**
* @author Vincent Touzet <vincent.touzet@gmail.com>
*/
interface SetupableTransportInterface
{
/**
* Setup the transport.
*/
public function setup(): void;
}

View File

@@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Sync;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
use Symfony\Component\Messenger\Stamp\SentStamp;
use Symfony\Component\Messenger\Transport\TransportInterface;
/**
* Transport that immediately marks messages as received and dispatches for handling.
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
class SyncTransport implements TransportInterface
{
private MessageBusInterface $messageBus;
public function __construct(MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
}
public function get(): iterable
{
throw new InvalidArgumentException('You cannot receive messages from the Messenger SyncTransport.');
}
public function ack(Envelope $envelope): void
{
throw new InvalidArgumentException('You cannot call ack() on the Messenger SyncTransport.');
}
public function reject(Envelope $envelope): void
{
throw new InvalidArgumentException('You cannot call reject() on the Messenger SyncTransport.');
}
public function send(Envelope $envelope): Envelope
{
/** @var SentStamp|null $sentStamp */
$sentStamp = $envelope->last(SentStamp::class);
$alias = null === $sentStamp ? 'sync' : ($sentStamp->getSenderAlias() ?: $sentStamp->getSenderClass());
$envelope = $envelope->with(new ReceivedStamp($alias));
return $this->messageBus->dispatch($envelope);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport\Sync;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
use Symfony\Component\Messenger\Transport\TransportFactoryInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
/**
* @author Ryan Weaver <ryan@symfonycasts.com>
*
* @implements TransportFactoryInterface<SyncTransport>
*/
class SyncTransportFactory implements TransportFactoryInterface
{
private MessageBusInterface $messageBus;
public function __construct(MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
}
public function createTransport(#[\SensitiveParameter] string $dsn, array $options, SerializerInterface $serializer): TransportInterface
{
return new SyncTransport($this->messageBus);
}
public function supports(#[\SensitiveParameter] string $dsn, array $options): bool
{
return str_starts_with($dsn, 'sync://');
}
}

View File

@@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Exception\InvalidArgumentException;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
/**
* @author Samuel Roze <samuel.roze@gmail.com>
*
* @implements TransportFactoryInterface<TransportInterface>
*/
class TransportFactory implements TransportFactoryInterface
{
private iterable $factories;
/**
* @param iterable<mixed, TransportFactoryInterface> $factories
*/
public function __construct(iterable $factories)
{
$this->factories = $factories;
}
public function createTransport(#[\SensitiveParameter] string $dsn, array $options, SerializerInterface $serializer): TransportInterface
{
foreach ($this->factories as $factory) {
if ($factory->supports($dsn, $options)) {
return $factory->createTransport($dsn, $options, $serializer);
}
}
// Help the user to select Symfony packages based on protocol.
$packageSuggestion = '';
if (str_starts_with($dsn, 'amqp://') || str_starts_with($dsn, 'amqps://')) {
$packageSuggestion = ' Run "composer require symfony/amqp-messenger" to install AMQP transport.';
} elseif (str_starts_with($dsn, 'doctrine://')) {
$packageSuggestion = ' Run "composer require symfony/doctrine-messenger" to install Doctrine transport.';
} elseif (str_starts_with($dsn, 'redis://') || str_starts_with($dsn, 'rediss://')) {
$packageSuggestion = ' Run "composer require symfony/redis-messenger" to install Redis transport.';
} elseif (str_starts_with($dsn, 'sqs://') || preg_match('#^https://sqs\.[\w\-]+\.amazonaws\.com/.+#', $dsn)) {
$packageSuggestion = ' Run "composer require symfony/amazon-sqs-messenger" to install Amazon SQS transport.';
} elseif (str_starts_with($dsn, 'beanstalkd://')) {
$packageSuggestion = ' Run "composer require symfony/beanstalkd-messenger" to install Beanstalkd transport.';
}
throw new InvalidArgumentException('No transport supports the given Messenger DSN.'.$packageSuggestion);
}
public function supports(#[\SensitiveParameter] string $dsn, array $options): bool
{
foreach ($this->factories as $factory) {
if ($factory->supports($dsn, $options)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
/**
* Creates a Messenger transport.
*
* @author Samuel Roze <samuel.roze@gmail.com>
*
* @template-covariant TTransport of TransportInterface
*/
interface TransportFactoryInterface
{
/**
* @return TTransport
*/
public function createTransport(#[\SensitiveParameter] string $dsn, array $options, SerializerInterface $serializer): TransportInterface;
public function supports(#[\SensitiveParameter] string $dsn, array $options): bool;
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Messenger\Transport;
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TransportInterface extends ReceiverInterface, SenderInterface
{
}