mirror of
https://github.com/itflow-org/itflow
synced 2026-09-22 06:31:15 +00:00
Update imapengine dependancies too
This commit is contained in:
@@ -9,7 +9,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
php: [8.5, 8.4, 8.3, 8.2, 8.1, 8.0]
|
||||
php: [8.5, 8.4, 8.3, 8.2, 8.1]
|
||||
stability: [prefer-stable]
|
||||
|
||||
name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
|
||||
|
||||
33
libs/vendor/zbateson/mail-mime-parser/README.md
vendored
33
libs/vendor/zbateson/mail-mime-parser/README.md
vendored
@@ -3,8 +3,6 @@
|
||||
Testable and PSR-compliant mail mime parser alternative to PHP's imap* functions and Pear libraries for reading messages in _Internet Message Format_ [RFC 822](http://tools.ietf.org/html/rfc822) (and later revisions [RFC 2822](http://tools.ietf.org/html/rfc2822), [RFC 5322](http://tools.ietf.org/html/rfc5322)).
|
||||
|
||||
[](https://github.com/zbateson/mail-mime-parser/actions/workflows/tests.yml)
|
||||
[](https://scrutinizer-ci.com/g/zbateson/mail-mime-parser/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/zbateson/mail-mime-parser/?branch=master)
|
||||
[](//packagist.org/packages/zbateson/mail-mime-parser)
|
||||
[](//packagist.org/packages/zbateson/mail-mime-parser)
|
||||
|
||||
@@ -28,23 +26,13 @@ A huge thank you to [all my sponsors](https://github.com/sponsors/zbateson). <3
|
||||
|
||||
If this project's helped you, please consider [sponsoring me](https://github.com/sponsors/zbateson).
|
||||
|
||||
## Php 7 Support Dropped
|
||||
## New in 4.0
|
||||
|
||||
As of mail-mime-parser 3.0, support for php 7 has been dropped.
|
||||
|
||||
## New in 3.0
|
||||
|
||||
Most changes in 3.0 are 'backend' changes, for example switching to PHP-DI for dependency injection, and basic usage should not be affected.
|
||||
|
||||
The header class method 'getAllParts' includes comment parts in 3.0.
|
||||
|
||||
Error, validation, and logging support has been added.
|
||||
|
||||
For a more complete list of changes, please visit the [3.0 Upgrade Guide](https://mail-mime-parser.org/upgrade-3.0) and the [Usage Guide](https://mail-mime-parser.org/).
|
||||
Version 4.0 requires PHP 8.1+ and focuses on API cleanup and improved configurability. For details, see the [4.0 Upgrade Guide](https://mail-mime-parser.org/upgrade-4.0).
|
||||
|
||||
## Requirements
|
||||
|
||||
MailMimeParser requires PHP 8.0 or newer. Tested on PHP 8.0, 8.1, 8.2, 8.3 and 8.4.
|
||||
MailMimeParser requires PHP 8.1 or newer. Tested on PHP 8.1, 8.2, 8.3, 8.4 and 8.5.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -109,16 +97,29 @@ fclose($handle);
|
||||
|
||||
```
|
||||
|
||||
## Encryption and Signing Plugins
|
||||
|
||||
Optional companion packages add S/MIME and PGP/MIME support for decrypting,
|
||||
encrypting, signing and verifying messages:
|
||||
|
||||
* [zbateson/mmp-crypt-smime](https://github.com/zbateson/mmp-crypt-smime) -- S/MIME via PHP's OpenSSL extension
|
||||
* [zbateson/mmp-crypt-gpg](https://github.com/zbateson/mmp-crypt-gpg) -- PGP/MIME via PEAR's Crypt_GPG
|
||||
|
||||
Install either package and encrypted/signed messages are automatically detected
|
||||
and decrypted during parsing. See the [Usage Guide](https://mail-mime-parser.org/#encryption-and-signing)
|
||||
for examples of reading encrypted messages and composing signed/encrypted ones.
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Usage Guide](https://mail-mime-parser.org/)
|
||||
* [API Reference](https://mail-mime-parser.org/api/3.0)
|
||||
* [API Reference](https://mail-mime-parser.org/api/4.0)
|
||||
|
||||
## Upgrade guides
|
||||
|
||||
* [1.x Upgrade Guide](https://mail-mime-parser.org/upgrade-1.0)
|
||||
* [2.x Upgrade Guide](https://mail-mime-parser.org/upgrade-2.0)
|
||||
* [3.x Upgrade Guide](https://mail-mime-parser.org/upgrade-3.0)
|
||||
* [4.x Upgrade Guide](https://mail-mime-parser.org/upgrade-4.0)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -19,17 +19,17 @@
|
||||
"docs": "https://mail-mime-parser.org/#usage-guide"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"guzzlehttp/psr7": "^2.5",
|
||||
"zbateson/mb-wrapper": "^2.0",
|
||||
"zbateson/stream-decorators": "^2.1",
|
||||
"php": ">=8.1",
|
||||
"guzzlehttp/psr7": "^2.5 || ^3.0",
|
||||
"zbateson/mb-wrapper": "^2.0 || ^3.0",
|
||||
"zbateson/stream-decorators": "^2.1 || ^3.0",
|
||||
"php-di/php-di": "^6.0|^7.0",
|
||||
"psr/log": "^1|^2|^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"friendsofphp/php-cs-fixer": "*",
|
||||
"phpstan/phpstan": "*",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"monolog/monolog": "^2|^3"
|
||||
},
|
||||
"suggest": {
|
||||
|
||||
@@ -19,26 +19,6 @@ use Throwable;
|
||||
*/
|
||||
class Error
|
||||
{
|
||||
/**
|
||||
* @var string The error message.
|
||||
*/
|
||||
protected string $message;
|
||||
|
||||
/**
|
||||
* @var string The PSR log level for this error.
|
||||
*/
|
||||
protected string $psrLevel;
|
||||
|
||||
/**
|
||||
* @var ErrorBag The object the error/notice occurred on.
|
||||
*/
|
||||
protected ErrorBag $object;
|
||||
|
||||
/**
|
||||
* @var ?Throwable An Exception object if one happened, or null if not
|
||||
*/
|
||||
protected ?Throwable $exception;
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
@@ -55,18 +35,18 @@ class Error
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws InvalidArgumentException if the passed $psrLogLevelAsErrorLevel
|
||||
* @throws InvalidArgumentException if the passed $psrLevel
|
||||
* is not a known PSR log level (see \Psr\Log\LogLevel)
|
||||
*/
|
||||
public function __construct(string $message, string $psrLogLevelAsErrorLevel, ErrorBag $object, ?Throwable $exception = null)
|
||||
{
|
||||
if (!isset($this->levelMap[$psrLogLevelAsErrorLevel])) {
|
||||
throw new InvalidArgumentException($psrLogLevelAsErrorLevel . ' is not a known PSR Log Level');
|
||||
public function __construct(
|
||||
protected readonly string $message,
|
||||
protected readonly string $psrLevel,
|
||||
protected readonly ErrorBag $object,
|
||||
protected readonly ?Throwable $exception = null
|
||||
) {
|
||||
if (!isset($this->levelMap[$psrLevel])) {
|
||||
throw new InvalidArgumentException($psrLevel . ' is not a known PSR Log Level');
|
||||
}
|
||||
$this->message = $message;
|
||||
$this->psrLevel = $psrLogLevelAsErrorLevel;
|
||||
$this->object = $object;
|
||||
$this->exception = $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,8 +18,6 @@ use Throwable;
|
||||
*/
|
||||
abstract class ErrorBag implements IErrorBag
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @var Error[] array of Error objects belonging to this object.
|
||||
*/
|
||||
@@ -30,9 +28,8 @@ abstract class ErrorBag implements IErrorBag
|
||||
*/
|
||||
private bool $validated = false;
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
public function __construct(protected LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,9 +92,7 @@ abstract class ErrorBag implements IErrorBag
|
||||
}
|
||||
return \array_values(\array_filter(
|
||||
$this->errors,
|
||||
function($e) use ($minPsrLevel) {
|
||||
return $e->isPsrLevelGreaterOrEqualTo($minPsrLevel);
|
||||
}
|
||||
fn($e) => $e->isPsrLevelGreaterOrEqualTo($minPsrLevel)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -109,9 +104,7 @@ abstract class ErrorBag implements IErrorBag
|
||||
public function getAllErrors(bool $validate = false, string $minPsrLevel = LogLevel::ERROR) : array
|
||||
{
|
||||
$arr = \array_values(\array_map(
|
||||
function($e) use ($validate, $minPsrLevel) {
|
||||
return $e->getAllErrors($validate, $minPsrLevel);
|
||||
},
|
||||
fn($e) => $e->getAllErrors($validate, $minPsrLevel),
|
||||
$this->getErrorBagChildren()
|
||||
));
|
||||
return \array_merge($this->getErrors($validate, $minPsrLevel), ...$arr);
|
||||
|
||||
@@ -50,6 +50,21 @@ abstract class AbstractHeader extends ErrorBag implements IHeader
|
||||
*/
|
||||
private ?array $comments = null;
|
||||
|
||||
/**
|
||||
* Resolves a nullable service from the global DI container, returning the
|
||||
* provided instance if non-null.
|
||||
*
|
||||
* @template T of object
|
||||
* @param T|null $service
|
||||
* @param class-string<T> $class
|
||||
* @return T
|
||||
*/
|
||||
protected static function resolveService(?object $service, string $class): object
|
||||
{
|
||||
/** @var T */
|
||||
return $service ?? MailMimeParser::getGlobalContainer()->get($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the header's name and raw value, then calls parseHeaderValue to
|
||||
* extract a parsed value.
|
||||
@@ -78,9 +93,7 @@ abstract class AbstractHeader extends ErrorBag implements IHeader
|
||||
*/
|
||||
protected function filterAndAssignToParts() : void
|
||||
{
|
||||
$this->parts = \array_values(\array_filter($this->allParts, function($p) {
|
||||
return !($p instanceof CommentPart);
|
||||
}));
|
||||
$this->parts = \array_values(\array_filter($this->allParts, fn($p) => !($p instanceof CommentPart)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +152,11 @@ abstract class AbstractHeader extends ErrorBag implements IHeader
|
||||
return $this->rawValue;
|
||||
}
|
||||
|
||||
public function getDecodedValue() : string
|
||||
{
|
||||
return \implode('', \array_map(fn(IHeaderPart $p) => $p->getValue(), $this->parts));
|
||||
}
|
||||
|
||||
public function getName() : string
|
||||
{
|
||||
return $this->name;
|
||||
|
||||
@@ -11,7 +11,6 @@ use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\AddressBaseConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Part\AddressGroupPart;
|
||||
use ZBateson\MailMimeParser\Header\Part\AddressPart;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
/**
|
||||
* A header containing one or more email addresses and/or groups of addresses.
|
||||
@@ -44,10 +43,9 @@ class AddressHeader extends AbstractHeader
|
||||
?LoggerInterface $logger = null,
|
||||
?AddressBaseConsumerService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$consumerService ?? $di->get(AddressBaseConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($consumerService, AddressBaseConsumerService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
@@ -55,7 +53,7 @@ class AddressHeader extends AbstractHeader
|
||||
|
||||
/**
|
||||
* Filters $this->allParts into the parts required by $this->parts
|
||||
* and assignes it.
|
||||
* and assigns it.
|
||||
*
|
||||
* The AbstractHeader::filterAndAssignToParts method filters out CommentParts.
|
||||
*/
|
||||
@@ -109,6 +107,30 @@ class AddressHeader extends AbstractHeader
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDecodedValue() : string
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($this->parts as $part) {
|
||||
if ($part instanceof AddressGroupPart) {
|
||||
$addrs = \array_map(fn(AddressPart $a) => $this->formatAddress($a), $part->getAddresses());
|
||||
$parts[] = $part->getName() . ': ' . \implode(', ', $addrs) . ';';
|
||||
} elseif ($part instanceof AddressPart) {
|
||||
$parts[] = $this->formatAddress($part);
|
||||
}
|
||||
}
|
||||
return \implode(', ', $parts);
|
||||
}
|
||||
|
||||
private function formatAddress(AddressPart $address) : string
|
||||
{
|
||||
$name = $address->getName();
|
||||
$email = $address->getEmail();
|
||||
if ($name !== '') {
|
||||
return $name . ' <' . $email . '>';
|
||||
}
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first email address in the header.
|
||||
*
|
||||
|
||||
@@ -25,19 +25,6 @@ use ZBateson\MailMimeParser\Header\Part\MimeToken;
|
||||
*/
|
||||
abstract class AbstractConsumerService implements IConsumerService
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @var HeaderPartFactory used to construct IHeaderPart objects
|
||||
*/
|
||||
protected HeaderPartFactory $partFactory;
|
||||
|
||||
/**
|
||||
* @var AbstractConsumerService[] array of sub-consumers used by this
|
||||
* consumer if any, or an empty array if none exist.
|
||||
*/
|
||||
protected array $subConsumers = [];
|
||||
|
||||
/**
|
||||
* @var ?string the generated token split pattern on first run, so it doesn't
|
||||
* need to be regenerated every time.
|
||||
@@ -47,11 +34,11 @@ abstract class AbstractConsumerService implements IConsumerService
|
||||
/**
|
||||
* @param AbstractConsumerService[] $subConsumers
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger, HeaderPartFactory $partFactory, array $subConsumers = [])
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->partFactory = $partFactory;
|
||||
$this->subConsumers = $subConsumers;
|
||||
public function __construct(
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly HeaderPartFactory $partFactory,
|
||||
protected array $subConsumers = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(string $value) : array
|
||||
|
||||
@@ -61,7 +61,7 @@ class AddressBaseConsumerService extends AbstractConsumerService
|
||||
* AddressBaseConsumerService doesn't have start/end tokens, and so always
|
||||
* returns false.
|
||||
*
|
||||
* @return false
|
||||
* @return bool
|
||||
*/
|
||||
protected function isEndToken(string $token) : bool
|
||||
{
|
||||
@@ -73,7 +73,7 @@ class AddressBaseConsumerService extends AbstractConsumerService
|
||||
* returns false.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return false
|
||||
* @return bool
|
||||
*/
|
||||
protected function isStartToken(string $token) : bool
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ZBateson\MailMimeParser\Header\Consumer;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Iterator;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MailMimeParser\Header\Part\AddressGroupPart;
|
||||
use ZBateson\MailMimeParser\Header\Part\HeaderPartFactory;
|
||||
|
||||
@@ -78,7 +79,7 @@ class AddressGroupConsumerService extends AddressBaseConsumerService
|
||||
* addresses, since a group could be empty.
|
||||
*
|
||||
* @param Iterator $tokens
|
||||
* @return IHeaderPart[]
|
||||
* @return \ZBateson\MailMimeParser\Header\IHeaderPart[]
|
||||
*/
|
||||
protected function parseTokensIntoParts(Iterator $tokens) : array
|
||||
{
|
||||
|
||||
@@ -39,14 +39,6 @@ use ZBateson\MailMimeParser\Header\Part\HeaderPartFactory;
|
||||
*/
|
||||
class GenericReceivedConsumerService extends AbstractGenericConsumerService
|
||||
{
|
||||
/**
|
||||
* @var string the current part name being parsed.
|
||||
*
|
||||
* This is always the lower-case name provided to the constructor, not the
|
||||
* actual string that started the consumer, which could be in any case.
|
||||
*/
|
||||
protected $partName;
|
||||
|
||||
/**
|
||||
* Constructor overridden to include $partName parameter.
|
||||
*
|
||||
@@ -55,10 +47,9 @@ class GenericReceivedConsumerService extends AbstractGenericConsumerService
|
||||
LoggerInterface $logger,
|
||||
HeaderPartFactory $partFactory,
|
||||
CommentConsumerService $commentConsumerService,
|
||||
string $partName
|
||||
protected readonly string $partName
|
||||
) {
|
||||
parent::__construct($logger, $partFactory, [$commentConsumerService]);
|
||||
$this->partName = $partName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,6 @@ use DateTimeImmutable;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\DateConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Part\DatePart;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
/**
|
||||
* Reads a DatePart value header in either RFC 2822 or RFC 822 format.
|
||||
@@ -27,10 +26,9 @@ class DateHeader extends AbstractHeader
|
||||
?LoggerInterface $logger = null,
|
||||
?DateConsumerService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$consumerService ?? $di->get(DateConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($consumerService, DateConsumerService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace ZBateson\MailMimeParser\Header;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\GenericConsumerMimeLiteralPartService;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
/**
|
||||
* Reads a generic header.
|
||||
@@ -27,20 +26,18 @@ class GenericHeader extends AbstractHeader
|
||||
?LoggerInterface $logger = null,
|
||||
?GenericConsumerMimeLiteralPartService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$consumerService ?? $di->get(DateConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($consumerService, GenericConsumerMimeLiteralPartService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
parent::__construct($logger, $consumerService, $name, $value);
|
||||
}
|
||||
|
||||
public function getValue() : ?string
|
||||
{
|
||||
if (!empty($this->parts)) {
|
||||
return \implode('', \array_map(function($p) { return $p->getValue(); }, $this->parts));
|
||||
return \implode('', \array_map(fn($p) => $p->getValue(), $this->parts));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ZBateson\MailMimeParser\Header;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\AddressBaseConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\DateConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\GenericConsumerMimeLiteralPartService;
|
||||
@@ -41,22 +42,15 @@ use ZBateson\MailMimeParser\Header\Part\MimeTokenPartFactory;
|
||||
*/
|
||||
class HeaderFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @var IConsumerService[] array of available consumer service classes
|
||||
*/
|
||||
protected array $consumerServices;
|
||||
|
||||
/**
|
||||
* @var MimeTokenPartFactory for mime decoding.
|
||||
*/
|
||||
protected MimeTokenPartFactory $mimeTokenPartFactory;
|
||||
|
||||
/**
|
||||
* @var string[][] maps IHeader types to headers.
|
||||
*/
|
||||
protected $types = [
|
||||
protected array $types = [
|
||||
AddressHeader::class => [
|
||||
'from',
|
||||
'to',
|
||||
@@ -106,11 +100,11 @@ class HeaderFactory
|
||||
* @var string Defines the generic IHeader type to use for headers that
|
||||
* aren't mapped in $types
|
||||
*/
|
||||
protected $genericType = GenericHeader::class;
|
||||
protected string $genericType = GenericHeader::class;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
MimeTokenPartFactory $mimeTokenPartFactory,
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly MimeTokenPartFactory $mimeTokenPartFactory,
|
||||
AddressBaseConsumerService $addressBaseConsumerService,
|
||||
DateConsumerService $dateConsumerService,
|
||||
GenericConsumerMimeLiteralPartService $genericConsumerMimeLiteralPartService,
|
||||
@@ -119,8 +113,6 @@ class HeaderFactory
|
||||
ReceivedConsumerService $receivedConsumerService,
|
||||
SubjectConsumerService $subjectConsumerService
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->mimeTokenPartFactory = $mimeTokenPartFactory;
|
||||
$this->consumerServices = [
|
||||
AddressBaseConsumerService::class => $addressBaseConsumerService,
|
||||
DateConsumerService::class => $dateConsumerService,
|
||||
@@ -195,19 +187,23 @@ class HeaderFactory
|
||||
$ref = new ReflectionClass($iHeaderClass);
|
||||
$params = $ref->getConstructor()->getParameters();
|
||||
if ($ref->isSubclassOf(MimeEncodedHeader::class)) {
|
||||
$type = $params[4]->getType();
|
||||
\assert($type instanceof ReflectionNamedType);
|
||||
return new $iHeaderClass(
|
||||
$name,
|
||||
$value,
|
||||
$this->logger,
|
||||
$this->mimeTokenPartFactory,
|
||||
$this->consumerServices[$params[4]->getType()->getName()]
|
||||
$this->consumerServices[$type->getName()]
|
||||
);
|
||||
}
|
||||
$type = $params[3]->getType();
|
||||
\assert($type instanceof ReflectionNamedType);
|
||||
return new $iHeaderClass(
|
||||
$name,
|
||||
$value,
|
||||
$this->logger,
|
||||
$this->consumerServices[$params[3]->getType()->getName()]
|
||||
$this->consumerServices[$type->getName()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ interface IHeader extends IErrorBag
|
||||
* or parameter headers (Content-Type), the 'value' is the value of the
|
||||
* first parsed part that isn't a comment.
|
||||
*
|
||||
* @return string The value
|
||||
* @return ?string The value, or null if the header has no parts.
|
||||
*/
|
||||
public function getValue() : ?string;
|
||||
|
||||
@@ -66,6 +66,19 @@ interface IHeader extends IErrorBag
|
||||
*/
|
||||
public function getRawValue() : string;
|
||||
|
||||
/**
|
||||
* Returns the full decoded and unfolded value of the header, reconstructed
|
||||
* from the parsed parts (excluding comments).
|
||||
*
|
||||
* Unlike getValue() which returns only the value of the first part, this
|
||||
* returns the full decoded representation. For example, for an
|
||||
* AddressHeader with `=?UTF-8?Q?J=C3=B6hn?= <john@example.com>`, this
|
||||
* would return `Jöhn <john@example.com>`.
|
||||
*
|
||||
* @return string The full decoded value.
|
||||
*/
|
||||
public function getDecodedValue() : string;
|
||||
|
||||
/**
|
||||
* Returns the name of the header.
|
||||
*
|
||||
|
||||
@@ -30,7 +30,7 @@ interface IHeaderPart extends IErrorBag, Stringable
|
||||
/**
|
||||
* Returns any CommentParts under this part container.
|
||||
*
|
||||
* @return CommentPart[]
|
||||
* @return Part\CommentPart[]
|
||||
*/
|
||||
public function getComments() : array;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\IdBaseConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Part\CommentPart;
|
||||
use ZBateson\MailMimeParser\Header\Part\MimeTokenPartFactory;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
/**
|
||||
* Represents a Content-ID, Message-ID, In-Reply-To or References header.
|
||||
@@ -31,11 +30,10 @@ class IdHeader extends MimeEncodedHeader
|
||||
?MimeTokenPartFactory $mimeTokenPartFactory = null,
|
||||
?IdBaseConsumerService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$mimeTokenPartFactory ?? $di->get(MimeTokenPartFactory::class),
|
||||
$consumerService ?? $di->get(IdBaseConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($mimeTokenPartFactory, MimeTokenPartFactory::class),
|
||||
self::resolveService($consumerService, IdBaseConsumerService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
@@ -60,12 +58,8 @@ class IdHeader extends MimeEncodedHeader
|
||||
public function getIds() : array
|
||||
{
|
||||
return \array_values(\array_map(
|
||||
function($p) {
|
||||
return $p->getValue();
|
||||
},
|
||||
\array_filter($this->parts, function($p) {
|
||||
return !($p instanceof CommentPart);
|
||||
})
|
||||
fn($p) => $p->getValue(),
|
||||
\array_filter($this->parts, fn($p) => !($p instanceof CommentPart))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,24 +21,18 @@ use ZBateson\MailMimeParser\Header\Part\MimeTokenPartFactory;
|
||||
abstract class MimeEncodedHeader extends AbstractHeader
|
||||
{
|
||||
/**
|
||||
* @var MimeTokenPartFactory for mime decoding.
|
||||
*/
|
||||
protected MimeTokenPartFactory $mimeTokenPartFactory;
|
||||
|
||||
/**
|
||||
* @var MimeLiteralPart[] the mime encoded parsed parts contained in this
|
||||
* @var IHeaderPart[] the mime encoded parsed parts contained in this
|
||||
* header
|
||||
*/
|
||||
protected $mimeEncodedParsedParts = [];
|
||||
protected array $mimeEncodedParsedParts = [];
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
MimeTokenPartFactory $mimeTokenPartFactory,
|
||||
protected readonly MimeTokenPartFactory $mimeTokenPartFactory,
|
||||
IConsumerService $consumerService,
|
||||
string $name,
|
||||
string $value
|
||||
) {
|
||||
$this->mimeTokenPartFactory = $mimeTokenPartFactory;
|
||||
parent::__construct($logger, $consumerService, $name, $value);
|
||||
}
|
||||
|
||||
@@ -52,7 +46,7 @@ abstract class MimeEncodedHeader extends AbstractHeader
|
||||
// whitespace between parts, etc...
|
||||
$matchp = '~(' . MimeToken::MIME_PART_PATTERN . ')~';
|
||||
$aMimeParts = \preg_split($matchp, $value, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
$this->mimeEncodedParsedParts = \array_map([$this->mimeTokenPartFactory, 'newInstance'], $aMimeParts);
|
||||
$this->mimeEncodedParsedParts = \array_map($this->mimeTokenPartFactory->newInstance(...), $aMimeParts);
|
||||
parent::parseHeaderValue(
|
||||
$consumer,
|
||||
\implode('', \array_map(fn ($part) => $part->getValue(), $this->mimeEncodedParsedParts))
|
||||
@@ -61,6 +55,6 @@ abstract class MimeEncodedHeader extends AbstractHeader
|
||||
|
||||
protected function getErrorBagChildren() : array
|
||||
{
|
||||
return \array_values(\array_filter(\array_merge($this->getAllParts(), $this->mimeEncodedParsedParts)));
|
||||
return \array_values(\array_merge($this->getAllParts(), $this->mimeEncodedParsedParts));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\IConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\ParameterConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Part\NameValuePart;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
/**
|
||||
* Represents a header containing an optional main value part and subsequent
|
||||
@@ -36,8 +35,8 @@ use ZBateson\MailMimeParser\MailMimeParser;
|
||||
class ParameterHeader extends AbstractHeader
|
||||
{
|
||||
/**
|
||||
* @var ParameterPart[] key map of lower-case parameter names and associated
|
||||
* ParameterParts.
|
||||
* @var array<string, NameValuePart> key map of lower-case parameter names and associated
|
||||
* NameValueParts.
|
||||
*/
|
||||
protected array $parameters = [];
|
||||
|
||||
@@ -47,10 +46,9 @@ class ParameterHeader extends AbstractHeader
|
||||
?LoggerInterface $logger = null,
|
||||
?ParameterConsumerService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$consumerService ?? $di->get(ParameterConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($consumerService, ParameterConsumerService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
@@ -70,6 +68,17 @@ class ParameterHeader extends AbstractHeader
|
||||
}
|
||||
}
|
||||
|
||||
public function getDecodedValue() : string
|
||||
{
|
||||
$value = $this->getValue() ?? '';
|
||||
foreach ($this->parameters as $param) {
|
||||
if ($param->getName() !== '') {
|
||||
$value .= '; ' . $param->getName() . '=' . $param->getValue();
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a parameter exists with the passed name.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ZBateson\MailMimeParser\Header\Part;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -27,8 +28,8 @@ class AddressGroupPart extends NameValuePart
|
||||
* Creates an AddressGroupPart out of the passed array of AddressParts/
|
||||
* AddressGroupParts and name.
|
||||
*
|
||||
* @param HeaderPart[] $nameParts
|
||||
* @param AddressPart[]|AddressGroupPart[] $addressesAndGroupParts
|
||||
* @param IHeaderPart[] $nameParts
|
||||
* @param IHeaderPart[] $addressesAndGroupParts
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
@@ -76,7 +77,7 @@ class AddressGroupPart extends NameValuePart
|
||||
|
||||
protected function validate() : void
|
||||
{
|
||||
if ($this->name === null || \mb_strlen($this->name) === 0) {
|
||||
if (\mb_strlen($this->name) === 0) {
|
||||
$this->addError('Address group doesn\'t have a name', LogLevel::ERROR);
|
||||
}
|
||||
if (empty($this->addresses)) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace ZBateson\MailMimeParser\Header\Part;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,9 @@ class CommentPart extends ContainerPart
|
||||
*/
|
||||
protected string $comment;
|
||||
|
||||
/**
|
||||
* @param array<IHeaderPart> $children
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
MbWrapper $charsetConverter,
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ZBateson\MailMimeParser\Header\Part;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\ErrorBag;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -27,11 +28,14 @@ use ZBateson\MbWrapper\MbWrapper;
|
||||
class ContainerPart extends HeaderPart
|
||||
{
|
||||
/**
|
||||
* @var HeaderPart[] parts that were used to create this part, collected for
|
||||
* @var IHeaderPart[] parts that were used to create this part, collected for
|
||||
* proper error reporting and validation.
|
||||
*/
|
||||
protected $children = [];
|
||||
protected array $children = [];
|
||||
|
||||
/**
|
||||
* @param array<IHeaderPart> $children
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
MbWrapper $charsetConverter,
|
||||
@@ -54,8 +58,8 @@ class ContainerPart extends HeaderPart
|
||||
* Spaces are removed if parts on either side of it have their
|
||||
* canIgnoreSpaceAfter/canIgnoreSpaceBefore properties set to true.
|
||||
*
|
||||
* @param HeaderPart[] $parts
|
||||
* @return HeaderPart[]
|
||||
* @param IHeaderPart[] $parts
|
||||
* @return IHeaderPart[]
|
||||
*/
|
||||
protected function filterIgnoredSpaces(array $parts) : array
|
||||
{
|
||||
@@ -91,7 +95,7 @@ class ContainerPart extends HeaderPart
|
||||
* The default implementation filters out ignorable whitespace between
|
||||
* parts, and concatenates parts calling 'getValue'.
|
||||
*
|
||||
* @param HeaderParts[] $parts
|
||||
* @param IHeaderPart[] $parts
|
||||
*/
|
||||
protected function getValueFromParts(array $parts) : string
|
||||
{
|
||||
@@ -119,10 +123,11 @@ class ContainerPart extends HeaderPart
|
||||
/**
|
||||
* Returns this part's children, same as getChildParts().
|
||||
*
|
||||
* @return ErrorBag
|
||||
* @return array<ErrorBag>
|
||||
*/
|
||||
protected function getErrorBagChildren() : array
|
||||
{
|
||||
/** @var array<ErrorBag> */
|
||||
return $this->children;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use DateTime;
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -31,7 +32,7 @@ class DatePart extends ContainerPart
|
||||
* an RFC 822 date, and failing that, tries to parse it by calling
|
||||
* new DateTime($value).
|
||||
*
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
|
||||
@@ -21,19 +21,6 @@ use ZBateson\MbWrapper\UnsupportedCharsetException;
|
||||
*/
|
||||
abstract class HeaderPart extends ErrorBag implements IHeaderPart
|
||||
{
|
||||
/**
|
||||
* @var string the representative value of the part after any conversion or
|
||||
* processing has been done on it (e.g. removing new lines, converting,
|
||||
* whatever else).
|
||||
*/
|
||||
protected string $value;
|
||||
|
||||
/**
|
||||
* @var MbWrapper $charsetConverter the charset converter used for
|
||||
* converting strings in HeaderPart::convertEncoding
|
||||
*/
|
||||
protected MbWrapper $charsetConverter;
|
||||
|
||||
/**
|
||||
* @var bool set to true to ignore spaces before this part
|
||||
*/
|
||||
@@ -49,11 +36,12 @@ abstract class HeaderPart extends ErrorBag implements IHeaderPart
|
||||
*/
|
||||
protected bool $isSpace = false;
|
||||
|
||||
public function __construct(LoggerInterface $logger, MbWrapper $charsetConverter, string $value)
|
||||
{
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
protected MbWrapper $charsetConverter,
|
||||
protected string $value
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
$this->charsetConverter = $charsetConverter;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +89,9 @@ abstract class HeaderPart extends ErrorBag implements IHeaderPart
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommentPart[]
|
||||
*/
|
||||
public function getComments() : array
|
||||
{
|
||||
return [];
|
||||
|
||||
@@ -18,18 +18,10 @@ use ZBateson\MbWrapper\MbWrapper;
|
||||
*/
|
||||
class HeaderPartFactory
|
||||
{
|
||||
/**
|
||||
* @var MbWrapper $charsetConverter passed to IHeaderPart constructors
|
||||
* for converting strings in IHeaderPart::convertEncoding
|
||||
*/
|
||||
protected MbWrapper $charsetConverter;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger, MbWrapper $charsetConverter)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->charsetConverter = $charsetConverter;
|
||||
public function __construct(
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly MbWrapper $charsetConverter
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +62,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new ContainerPart.
|
||||
*
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function newContainerPart(array $children) : ContainerPart
|
||||
{
|
||||
@@ -90,7 +82,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new QuotedLiteralPart.
|
||||
*
|
||||
* @param HeaderPart[] $parts
|
||||
* @param IHeaderPart[] $parts
|
||||
*/
|
||||
public function newQuotedLiteralPart(array $parts) : QuotedLiteralPart
|
||||
{
|
||||
@@ -100,7 +92,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new CommentPart.
|
||||
*
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function newCommentPart(array $children) : CommentPart
|
||||
{
|
||||
@@ -110,8 +102,8 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new AddressPart.
|
||||
*
|
||||
* @param HeaderPart[] $nameParts
|
||||
* @param HeaderPart[] $emailParts
|
||||
* @param IHeaderPart[] $nameParts
|
||||
* @param IHeaderPart[] $emailParts
|
||||
*/
|
||||
public function newAddress(array $nameParts, array $emailParts) : AddressPart
|
||||
{
|
||||
@@ -121,8 +113,8 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new AddressGroupPart
|
||||
*
|
||||
* @param HeaderPart[] $nameParts
|
||||
* @param AddressPart[]|AddressGroupPart[] $addressesAndGroups
|
||||
* @param IHeaderPart[] $nameParts
|
||||
* @param IHeaderPart[] $addressesAndGroups
|
||||
*/
|
||||
public function newAddressGroupPart(array $nameParts, array $addressesAndGroups) : AddressGroupPart
|
||||
{
|
||||
@@ -132,7 +124,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new DatePart
|
||||
*
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function newDatePart(array $children) : DatePart
|
||||
{
|
||||
@@ -142,7 +134,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new ParameterPart.
|
||||
*
|
||||
* @param HeaderPart[] $nameParts
|
||||
* @param IHeaderPart[] $nameParts
|
||||
*/
|
||||
public function newParameterPart(array $nameParts, ContainerPart $valuePart) : ParameterPart
|
||||
{
|
||||
@@ -152,7 +144,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new ReceivedPart.
|
||||
*
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function newReceivedPart(string $name, array $children) : ReceivedPart
|
||||
{
|
||||
@@ -162,7 +154,7 @@ class HeaderPartFactory
|
||||
/**
|
||||
* Initializes and returns a new ReceivedDomainPart.
|
||||
*
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function newReceivedDomainPart(string $name, array $children) : ReceivedDomainPart
|
||||
{
|
||||
|
||||
@@ -81,10 +81,7 @@ class MimeToken extends Token
|
||||
}
|
||||
$this->charset = $matches[1];
|
||||
$this->language = (!empty($matches[2])) ? $matches[2] : null;
|
||||
if ($this->charset !== null) {
|
||||
return $this->convertEncoding($body, $this->charset, true);
|
||||
}
|
||||
return $this->convertEncoding($body, 'ISO-8859-1', true);
|
||||
return $this->convertEncoding($body, $this->charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace ZBateson\MailMimeParser\Header\Part;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use ZBateson\MailMimeParser\ErrorBag;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -24,6 +25,10 @@ class NameValuePart extends ContainerPart
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* @param array<IHeaderPart> $nameParts
|
||||
* @param array<IHeaderPart> $valueParts
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
MbWrapper $charsetConverter,
|
||||
@@ -41,7 +46,7 @@ class NameValuePart extends ContainerPart
|
||||
* Creates the string 'name' representation of this part constructed from
|
||||
* the child name parts passed to it.
|
||||
*
|
||||
* @param HeaderParts[] $parts
|
||||
* @param IHeaderPart[] $parts
|
||||
*/
|
||||
protected function getNameFromParts(array $parts) : string
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace ZBateson\MailMimeParser\Header\Part;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ class ParameterPart extends NameValuePart
|
||||
protected bool $encoded = false;
|
||||
|
||||
/**
|
||||
* @param HeaderPart[] $nameParts
|
||||
* @param IHeaderPart[] $nameParts
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace ZBateson\MailMimeParser\Header\Part;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -48,7 +49,7 @@ class ReceivedDomainPart extends ReceivedPart
|
||||
protected ?string $address = null;
|
||||
|
||||
/**
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace ZBateson\MailMimeParser\Header\Part;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\IHeaderPart;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
/**
|
||||
@@ -23,7 +24,7 @@ use ZBateson\MbWrapper\MbWrapper;
|
||||
class ReceivedPart extends NameValuePart
|
||||
{
|
||||
/**
|
||||
* @param HeaderPart[] $children
|
||||
* @param IHeaderPart[] $children
|
||||
*/
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
|
||||
@@ -47,6 +47,9 @@ class SplitParameterPart extends ParameterPart
|
||||
return $parts[0]->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<MimeToken|Token>
|
||||
*/
|
||||
private function getMimeTokens(string $value) : array
|
||||
{
|
||||
$pattern = MimeToken::MIME_PART_PATTERN;
|
||||
@@ -60,6 +63,10 @@ class SplitParameterPart extends ParameterPart
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<ParameterPart> $parts
|
||||
* @return array<ParameterPart|MimeToken|Token>
|
||||
*/
|
||||
private function combineAdjacentUnencodedParts(array $parts) : array
|
||||
{
|
||||
$runningValue = '';
|
||||
@@ -81,6 +88,9 @@ class SplitParameterPart extends ParameterPart
|
||||
return $returnedParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<ParameterPart> $parts
|
||||
*/
|
||||
protected function getValueFromParts(array $parts) : string
|
||||
{
|
||||
$sorted = $parts;
|
||||
|
||||
@@ -11,7 +11,7 @@ use DateTime;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\ReceivedConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Part\DatePart;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
use ZBateson\MailMimeParser\Header\Part\ReceivedDomainPart;
|
||||
|
||||
/**
|
||||
* Represents a Received header.
|
||||
@@ -80,6 +80,12 @@ use ZBateson\MailMimeParser\MailMimeParser;
|
||||
*/
|
||||
class ReceivedHeader extends ParameterHeader
|
||||
{
|
||||
/**
|
||||
* @var array<string, ReceivedDomainPart> key map of lower-case parameter names and associated
|
||||
* ReceivedDomainParts.
|
||||
*/
|
||||
protected array $parameters = [];
|
||||
|
||||
/**
|
||||
* @var DateTime the date/time stamp in the header.
|
||||
*/
|
||||
@@ -96,10 +102,9 @@ class ReceivedHeader extends ParameterHeader
|
||||
?LoggerInterface $logger = null,
|
||||
?ReceivedConsumerService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
AbstractHeader::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$consumerService ?? $di->get(ReceivedConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($consumerService, ReceivedConsumerService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace ZBateson\MailMimeParser\Header;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\SubjectConsumerService;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
/**
|
||||
* Reads a subject header.
|
||||
@@ -27,10 +26,9 @@ class SubjectHeader extends AbstractHeader
|
||||
?LoggerInterface $logger = null,
|
||||
?SubjectConsumerService $consumerService = null
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$consumerService ?? $di->get(SubjectConsumerService::class),
|
||||
self::resolveService($logger, LoggerInterface::class),
|
||||
self::resolveService($consumerService, SubjectConsumerService::class),
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
|
||||
@@ -66,7 +66,7 @@ interface IMessage extends IMimePart
|
||||
* returned.
|
||||
*
|
||||
* @see IMessage::getHtmlStream() to get the html content stream directly.
|
||||
* @see IMessage::getHtmlStream() to get the html content in a string.
|
||||
* @see IMessage::getHtmlContent() to get the html content in a string.
|
||||
* @see IMessage::getTextPart() to get the text part(s).
|
||||
* @see IMessage::getTextPartCount() to get a count of text parts.
|
||||
* @see IMessage::getHtmlPartCount() to get a count of html parts.
|
||||
@@ -191,15 +191,12 @@ interface IMessage extends IMimePart
|
||||
* Removes the text/plain part of the message at the passed index if one
|
||||
* exists (defaults to first part if an index isn't passed).
|
||||
*
|
||||
* Returns true if a part exists at the passed index and has been removed.
|
||||
*
|
||||
* @see IMessage::setTextPart() to set the text part
|
||||
* @see IMessage::removeHtmlPart() to remove an html part
|
||||
* @see IMessage::removeAllTextParts() to remove all text parts
|
||||
* @param int $index Optional 0-based index of inline text part to remove.
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function removeTextPart(int $index = 0) : bool;
|
||||
public function removeTextPart(int $index = 0) : static;
|
||||
|
||||
/**
|
||||
* Removes all text/plain inline parts in this message.
|
||||
@@ -225,23 +222,19 @@ interface IMessage extends IMimePart
|
||||
* @see IMessage::removeAllHtmlParts() to remove all html parts
|
||||
* @param bool $moveRelatedPartsBelowMessage Optionally pass false to remove
|
||||
* related parts.
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function removeAllTextParts(bool $moveRelatedPartsBelowMessage = true) : bool;
|
||||
public function removeAllTextParts(bool $moveRelatedPartsBelowMessage = true) : static;
|
||||
|
||||
/**
|
||||
* Removes the text/html part of the message at the passed index if one
|
||||
* exists (defaults to first part if an index isn't passed).
|
||||
*
|
||||
* Returns true if a part exists at the passed index and has been removed.
|
||||
*
|
||||
* @see IMessage::setHtmlPart() to set the html part
|
||||
* @see IMessage::removeTextPart() to remove a text part
|
||||
* @see IMessage::removeAllHtmlParts() to remove all html parts
|
||||
* @param int $index Optional 0-based index of inline html part to remove.
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function removeHtmlPart(int $index = 0) : bool;
|
||||
public function removeHtmlPart(int $index = 0) : static;
|
||||
|
||||
/**
|
||||
* Removes all text/html inline parts in this message.
|
||||
@@ -264,12 +257,11 @@ interface IMessage extends IMimePart
|
||||
*
|
||||
* @see IMessage::setHtmlPart() to set the html part
|
||||
* @see IMessage::removeHtmlPart() to remove an html part
|
||||
* @see IMessage::removeAllTextParts() to remove all html parts
|
||||
* @see IMessage::removeAllTextParts() to remove all text parts
|
||||
* @param bool $moveRelatedPartsBelowMessage Optionally pass false to remove
|
||||
* related parts.
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function removeAllHtmlParts(bool $moveRelatedPartsBelowMessage = true) : bool;
|
||||
public function removeAllHtmlParts(bool $moveRelatedPartsBelowMessage = true) : static;
|
||||
|
||||
/**
|
||||
* Returns the attachment part at the given 0-based index, or null if none
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the ZBateson\MailMimeParser project.
|
||||
*
|
||||
@@ -7,6 +8,7 @@
|
||||
|
||||
namespace ZBateson\MailMimeParser;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use DI\Container;
|
||||
use DI\ContainerBuilder;
|
||||
use DI\Definition\Source\DefinitionSource;
|
||||
@@ -61,6 +63,12 @@ class MailMimeParser
|
||||
*/
|
||||
private const DEFAULT_DEFINITIONS_FILE = __DIR__ . '/di_config.php';
|
||||
|
||||
/**
|
||||
* The key in a package's composer.json "extra" section that MMP looks
|
||||
* for to auto-discover plugin DI configurations.
|
||||
*/
|
||||
private const PLUGIN_EXTRA_KEY = 'mail-mime-parser';
|
||||
|
||||
/**
|
||||
* @var Container The instance's dependency injection container.
|
||||
*/
|
||||
@@ -77,13 +85,15 @@ class MailMimeParser
|
||||
private static ?Container $globalContainer = null;
|
||||
|
||||
/**
|
||||
* @var array<array|string|DefinitionSource> an array of global definitions
|
||||
* @var array<array<string, mixed>|string|DefinitionSource> an array of global definitions
|
||||
* being used.
|
||||
*/
|
||||
private static array $globalDefinitions = [self::DEFAULT_DEFINITIONS_FILE];
|
||||
|
||||
/**
|
||||
* Returns the default ContainerBuilder with default loaded definitions.
|
||||
*
|
||||
* @return ContainerBuilder<Container>
|
||||
*/
|
||||
private static function getGlobalContainerBuilder() : ContainerBuilder
|
||||
{
|
||||
@@ -91,15 +101,77 @@ class MailMimeParser
|
||||
foreach (self::$globalDefinitions as $def) {
|
||||
$builder->addDefinitions($def);
|
||||
}
|
||||
foreach (self::discoverPluginConfigs() as $configFile) {
|
||||
$builder->addDefinitions($configFile);
|
||||
}
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovers plugin DI config files from installed Composer packages.
|
||||
*
|
||||
* Uses Composer's InstalledVersions runtime API to enumerate installed
|
||||
* packages and reads each package's composer.json for the
|
||||
* "extra.mail-mime-parser.di_config" entry.
|
||||
*
|
||||
* Returns an empty array in non-Composer environments.
|
||||
*
|
||||
* @return string[] Absolute paths to discovered config files
|
||||
*/
|
||||
private static function discoverPluginConfigs() : array
|
||||
{
|
||||
if (!\class_exists(InstalledVersions::class)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$configs = [];
|
||||
foreach (InstalledVersions::getInstalledPackages() as $packageName) {
|
||||
$installPath = InstalledVersions::getInstallPath($packageName);
|
||||
if ($installPath === null) {
|
||||
continue;
|
||||
}
|
||||
$configFile = self::readPluginConfigPath($installPath);
|
||||
if ($configFile !== null) {
|
||||
$configs[] = $configFile;
|
||||
}
|
||||
}
|
||||
return $configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a package's composer.json and returns the absolute path to its
|
||||
* MMP DI config file, or null if the package is not an MMP plugin.
|
||||
*
|
||||
* Looks for "extra.mail-mime-parser.di_config" in the package's
|
||||
* composer.json.
|
||||
*/
|
||||
public static function readPluginConfigPath(string $packageInstallPath) : ?string
|
||||
{
|
||||
$composerJsonPath = $packageInstallPath . '/composer.json';
|
||||
if (!\file_exists($composerJsonPath)) {
|
||||
return null;
|
||||
}
|
||||
$data = \json_decode(\file_get_contents($composerJsonPath), true);
|
||||
if (!\is_array($data)) {
|
||||
return null;
|
||||
}
|
||||
$extra = $data['extra'][self::PLUGIN_EXTRA_KEY] ?? null;
|
||||
if (!\is_array($extra) || !isset($extra['di_config'])) {
|
||||
return null;
|
||||
}
|
||||
$configFile = \realpath($packageInstallPath . '/' . $extra['di_config']);
|
||||
if ($configFile === false || !\file_exists($configFile)) {
|
||||
return null;
|
||||
}
|
||||
return $configFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets global configuration for php-di. Overrides all previously set
|
||||
* definitions. You can optionally not use the default MMP definitions file
|
||||
* by passing 'false' to the $useDefaultDefinitionsFile argument.
|
||||
*
|
||||
* @var array<array|string|DefinitionSource> array of definitions
|
||||
* @param array<array<string, mixed>|string|DefinitionSource> $phpDiConfigs array of definitions
|
||||
*/
|
||||
public static function setGlobalPhpDiConfigurations(array $phpDiConfigs, bool $useDefaultDefinitionsFile = true) : void
|
||||
{
|
||||
@@ -110,6 +182,9 @@ class MailMimeParser
|
||||
self::$globalContainer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|string|DefinitionSource $phpDiConfig
|
||||
*/
|
||||
public static function addGlobalPhpDiContainerDefinition(array|string|DefinitionSource $phpDiConfig) : void
|
||||
{
|
||||
self::$globalDefinitions[] = $phpDiConfig;
|
||||
@@ -135,6 +210,19 @@ class MailMimeParser
|
||||
return self::$globalContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fallback charset used for text/* content parts that don't
|
||||
* declare a charset. Defaults to 'ISO-8859-1' per RFC 2045.
|
||||
*
|
||||
* Many modern messages omit the charset and are actually UTF-8, so you
|
||||
* may want to set this to 'UTF-8'.
|
||||
*/
|
||||
public static function setFallbackCharset(string $charset) : void
|
||||
{
|
||||
self::$globalDefinitions[] = ['defaultFallbackCharset' => $charset];
|
||||
self::$globalContainer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the provided logger globally.
|
||||
*/
|
||||
@@ -162,6 +250,8 @@ class MailMimeParser
|
||||
* @see MailMimeParser::setGlobalPhpDiConfiguration() to register
|
||||
* configuration globally.
|
||||
* @see MailMimeParser::setGlobalLogger() to set a global logger
|
||||
*
|
||||
* @param array<string, mixed>|string|DefinitionSource|null $phpDiContainerConfig
|
||||
*/
|
||||
public function __construct(
|
||||
?LoggerInterface $logger = null,
|
||||
@@ -194,22 +284,21 @@ class MailMimeParser
|
||||
*
|
||||
* If the passed $resource is a resource handle or StreamInterface, the
|
||||
* resource must remain open while the returned IMessage object exists.
|
||||
* Pass true as the second argument to have the resource attached to the
|
||||
* IMessage and closed for you when it's destroyed, or pass false to
|
||||
* manually close it if it should remain open after the IMessage object is
|
||||
* destroyed.
|
||||
* Pass true as the second argument to have the resource automatically
|
||||
* closed when the returned IMessage is destroyed, or pass false to
|
||||
* manage the resource lifecycle yourself.
|
||||
*
|
||||
* @param resource|StreamInterface|string $resource The resource handle to
|
||||
* the input stream of the mime message, or a string containing a
|
||||
* mime message.
|
||||
* @param bool $attached pass true to have it attached to the returned
|
||||
* IMessage and destroyed with it.
|
||||
* @param bool $autoClose pass true to have the resource closed
|
||||
* automatically when the returned IMessage is destroyed.
|
||||
*/
|
||||
public function parse(mixed $resource, bool $attached) : IMessage
|
||||
public function parse(mixed $resource, bool $autoClose) : IMessage
|
||||
{
|
||||
$stream = Utils::streamFor(
|
||||
$resource,
|
||||
['metadata' => ['mmp-detached-stream' => ($attached !== true)]]
|
||||
['metadata' => ['mmp-detached-stream' => ($autoClose !== true)]]
|
||||
);
|
||||
if (!$stream->isSeekable()) {
|
||||
$stream = new CachingStream($stream);
|
||||
|
||||
@@ -47,14 +47,16 @@ class Message extends MimePart implements IMessage
|
||||
?PartHeaderContainer $headerContainer = null,
|
||||
?PartChildrenContainer $partChildrenContainer = null,
|
||||
?MultipartHelper $multipartHelper = null,
|
||||
?PrivacyHelper $privacyHelper = null
|
||||
?PrivacyHelper $privacyHelper = null,
|
||||
string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
parent::__construct(
|
||||
null,
|
||||
$logger,
|
||||
$streamContainer,
|
||||
$headerContainer,
|
||||
$partChildrenContainer
|
||||
$partChildrenContainer,
|
||||
$defaultFallbackCharset
|
||||
);
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
$this->multipartHelper = $multipartHelper ?? $di->get(MultipartHelper::class);
|
||||
@@ -67,24 +69,23 @@ class Message extends MimePart implements IMessage
|
||||
*
|
||||
* If the passed $resource is a resource handle or StreamInterface, the
|
||||
* resource must remain open while the returned IMessage object exists.
|
||||
* Pass true as the second argument to have the resource attached to the
|
||||
* IMessage and closed for you when it's destroyed, or pass false to
|
||||
* manually close it if it should remain open after the IMessage object is
|
||||
* destroyed.
|
||||
* Pass true as the second argument to have the resource automatically
|
||||
* closed when the returned IMessage is destroyed, or pass false to
|
||||
* manage the resource lifecycle yourself.
|
||||
*
|
||||
* @param resource|StreamInterface|string $resource The resource handle to
|
||||
* the input stream of the mime message, or a string containing a
|
||||
* mime message.
|
||||
* @param bool $attached pass true to have it attached to the returned
|
||||
* IMessage and destroyed with it.
|
||||
* @param bool $autoClose pass true to have the resource closed
|
||||
* automatically when the returned IMessage is destroyed.
|
||||
*/
|
||||
public static function from(mixed $resource, bool $attached) : IMessage
|
||||
public static function from(mixed $resource, bool $autoClose) : IMessage
|
||||
{
|
||||
static $mmp = null;
|
||||
if ($mmp === null) {
|
||||
$mmp = new MailMimeParser();
|
||||
}
|
||||
return $mmp->parse($resource, $attached);
|
||||
return $mmp->parse($resource, $autoClose);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,44 +197,48 @@ class Message extends MimePart implements IMessage
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeTextPart(int $index = 0) : bool
|
||||
public function removeTextPart(int $index = 0) : static
|
||||
{
|
||||
return $this->multipartHelper
|
||||
$this->multipartHelper
|
||||
->removePartByMimeType(
|
||||
$this,
|
||||
'text/plain',
|
||||
$index
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAllTextParts(bool $moveRelatedPartsBelowMessage = true) : bool
|
||||
public function removeAllTextParts(bool $moveRelatedPartsBelowMessage = true) : static
|
||||
{
|
||||
return $this->multipartHelper
|
||||
$this->multipartHelper
|
||||
->removeAllContentPartsByMimeType(
|
||||
$this,
|
||||
'text/plain',
|
||||
$moveRelatedPartsBelowMessage
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeHtmlPart(int $index = 0) : bool
|
||||
public function removeHtmlPart(int $index = 0) : static
|
||||
{
|
||||
return $this->multipartHelper
|
||||
$this->multipartHelper
|
||||
->removePartByMimeType(
|
||||
$this,
|
||||
'text/html',
|
||||
$index
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAllHtmlParts(bool $moveRelatedPartsBelowMessage = true) : bool
|
||||
public function removeAllHtmlParts(bool $moveRelatedPartsBelowMessage = true) : static
|
||||
{
|
||||
return $this->multipartHelper
|
||||
$this->multipartHelper
|
||||
->removeAllContentPartsByMimeType(
|
||||
$this,
|
||||
'text/html',
|
||||
$moveRelatedPartsBelowMessage
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAttachmentPart(int $index) : ?IMessagePart
|
||||
@@ -332,11 +337,12 @@ class Message extends MimePart implements IMessage
|
||||
{
|
||||
$params = '';
|
||||
if (!empty($this->getMessageId())) {
|
||||
$params .= ', message-id=' . $this->getContentId();
|
||||
$params .= ', message-id=' . $this->getMessageId();
|
||||
}
|
||||
$params .= ', content-type=' . $this->getContentType();
|
||||
$nsClass = static::class;
|
||||
$class = \substr($nsClass, (\strrpos($nsClass, '\\') ?? -1) + 1);
|
||||
$pos = \strrpos($nsClass, '\\');
|
||||
$class = ($pos !== false) ? \substr($nsClass, $pos + 1) : $nsClass;
|
||||
return $class . '(' . \spl_object_id($this) . $params . ')';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,12 @@ use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
*/
|
||||
abstract class IMessagePartFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
protected StreamFactory $streamFactory;
|
||||
|
||||
protected PartStreamContainerFactory $partStreamContainerFactory;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $streamFactory,
|
||||
PartStreamContainerFactory $partStreamContainerFactory
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly StreamFactory $streamFactory,
|
||||
protected readonly PartStreamContainerFactory $partStreamContainerFactory,
|
||||
protected readonly string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->partStreamContainerFactory = $partStreamContainerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,20 +19,15 @@ use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
*/
|
||||
class IMimePartFactory extends IMessagePartFactory
|
||||
{
|
||||
protected PartHeaderContainerFactory $partHeaderContainerFactory;
|
||||
|
||||
protected PartChildrenContainerFactory $partChildrenContainerFactory;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $streamFactory,
|
||||
PartStreamContainerFactory $partStreamContainerFactory,
|
||||
PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
PartChildrenContainerFactory $partChildrenContainerFactory
|
||||
protected readonly PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
protected readonly PartChildrenContainerFactory $partChildrenContainerFactory,
|
||||
string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
parent::__construct($logger, $streamFactory, $partStreamContainerFactory);
|
||||
$this->partHeaderContainerFactory = $partHeaderContainerFactory;
|
||||
$this->partChildrenContainerFactory = $partChildrenContainerFactory;
|
||||
parent::__construct($logger, $streamFactory, $partStreamContainerFactory, $defaultFallbackCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +42,8 @@ class IMimePartFactory extends IMessagePartFactory
|
||||
$this->logger,
|
||||
$streamContainer,
|
||||
$headerContainer,
|
||||
$this->partChildrenContainerFactory->newInstance()
|
||||
$this->partChildrenContainerFactory->newInstance(),
|
||||
$this->defaultFallbackCharset
|
||||
);
|
||||
$streamContainer->setStream($this->streamFactory->newMessagePartStream($part));
|
||||
return $part;
|
||||
|
||||
@@ -29,7 +29,8 @@ class IUUEncodedPartFactory extends IMessagePartFactory
|
||||
null,
|
||||
$parent,
|
||||
$this->logger,
|
||||
$streamContainer
|
||||
$streamContainer,
|
||||
$this->defaultFallbackCharset
|
||||
);
|
||||
$streamContainer->setStream($this->streamFactory->newMessagePartStream($part));
|
||||
return $part;
|
||||
|
||||
@@ -18,21 +18,10 @@ use ZBateson\MailMimeParser\Message\PartHeaderContainer;
|
||||
*/
|
||||
class PartHeaderContainerFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @var HeaderFactory the HeaderFactory passed to HeaderContainer instances.
|
||||
*/
|
||||
protected HeaderFactory $headerFactory;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger, HeaderFactory $headerFactory)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->headerFactory = $headerFactory;
|
||||
public function __construct(
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly HeaderFactory $headerFactory
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,24 +19,12 @@ use ZBateson\MbWrapper\MbWrapper;
|
||||
*/
|
||||
class PartStreamContainerFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
protected StreamFactory $streamFactory;
|
||||
|
||||
protected MbWrapper $mbWrapper;
|
||||
|
||||
protected bool $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $streamFactory,
|
||||
MbWrapper $mbWrapper,
|
||||
bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly StreamFactory $streamFactory,
|
||||
protected readonly MbWrapper $mbWrapper,
|
||||
protected readonly bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->mbWrapper = $mbWrapper;
|
||||
$this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
}
|
||||
|
||||
public function newInstance() : PartStreamContainer
|
||||
|
||||
@@ -17,21 +17,9 @@ use ZBateson\MailMimeParser\Message\Factory\IUUEncodedPartFactory;
|
||||
*/
|
||||
abstract class AbstractHelper
|
||||
{
|
||||
/**
|
||||
* @var IMimePartFactory to create parts for attachments/content
|
||||
*/
|
||||
protected IMimePartFactory $mimePartFactory;
|
||||
|
||||
/**
|
||||
* @var IUUEncodedPartFactory to create parts for attachments
|
||||
*/
|
||||
protected IUUEncodedPartFactory $uuEncodedPartFactory;
|
||||
|
||||
public function __construct(
|
||||
IMimePartFactory $mimePartFactory,
|
||||
IUUEncodedPartFactory $uuEncodedPartFactory
|
||||
protected readonly IMimePartFactory $mimePartFactory,
|
||||
protected readonly IUUEncodedPartFactory $uuEncodedPartFactory
|
||||
) {
|
||||
$this->mimePartFactory = $mimePartFactory;
|
||||
$this->uuEncodedPartFactory = $uuEncodedPartFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use ZBateson\MailMimeParser\Header\HeaderConsts;
|
||||
use ZBateson\MailMimeParser\Header\IHeader;
|
||||
use ZBateson\MailMimeParser\IMessage;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
use ZBateson\MailMimeParser\Message\IMessagePart;
|
||||
use ZBateson\MailMimeParser\Message\IMimePart;
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,9 @@ class GenericHelper extends AbstractHelper
|
||||
* one defined in the static $nonMimeContentFields
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @param string[] $exceptions
|
||||
*/
|
||||
private function isMimeContentField(IHeader $header, array $exceptions = []) : bool
|
||||
{
|
||||
return (\stripos($header->getName(), 'Content') === 0
|
||||
@@ -145,15 +149,20 @@ class GenericHelper extends AbstractHelper
|
||||
* replaced, and instead $replacement's type headers are copied to $message,
|
||||
* and any children below $replacement are added directly below $message.
|
||||
*/
|
||||
public function replacePart(IMessage $message, IMimePart $part, IMimePart $replacement) : static
|
||||
public function replacePart(IMessage $message, IMessagePart $part, IMessagePart $replacement) : static
|
||||
{
|
||||
$position = $message->removePart($replacement);
|
||||
$replacementParent = $replacement->getParent();
|
||||
$position = ($replacementParent !== null)
|
||||
? \array_search($replacement, $replacementParent->getChildParts(), true)
|
||||
: false;
|
||||
$message->removePart($replacement);
|
||||
if ($part === $message) {
|
||||
\assert($replacement instanceof IMimePart);
|
||||
$this->movePartContentAndChildren($replacement, $message);
|
||||
return $this;
|
||||
}
|
||||
$parent = $part->getParent();
|
||||
$parent->addChild($replacement, $position);
|
||||
$parent->addChild($replacement, $position !== false ? $position : null);
|
||||
$parent->removePart($part);
|
||||
|
||||
return $this;
|
||||
|
||||
@@ -25,18 +25,12 @@ use ZBateson\MailMimeParser\Message\PartFilter;
|
||||
*/
|
||||
class MultipartHelper extends AbstractHelper
|
||||
{
|
||||
/**
|
||||
* @var GenericHelper a GenericHelper instance
|
||||
*/
|
||||
private GenericHelper $genericHelper;
|
||||
|
||||
public function __construct(
|
||||
IMimePartFactory $mimePartFactory,
|
||||
IUUEncodedPartFactory $uuEncodedPartFactory,
|
||||
GenericHelper $genericHelper
|
||||
private readonly GenericHelper $genericHelper
|
||||
) {
|
||||
parent::__construct($mimePartFactory, $uuEncodedPartFactory);
|
||||
$this->genericHelper = $genericHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,9 +111,9 @@ class MultipartHelper extends AbstractHelper
|
||||
* @param string $mimeType the content-type to find below $alternativePart
|
||||
* @param IMimePart $alternativePart The multipart/alternative part to look
|
||||
* under
|
||||
* @return bool|IMimePart false if a part is not found
|
||||
* @return IMimePart|false false if a part is not found
|
||||
*/
|
||||
public function getContentPartContainerFromAlternative($mimeType, IMimePart $alternativePart) : bool|IMimePart
|
||||
public function getContentPartContainerFromAlternative($mimeType, IMimePart $alternativePart) : IMimePart|false
|
||||
{
|
||||
$part = $alternativePart->getPart(0, PartFilter::fromInlineContentType($mimeType));
|
||||
$contPart = null;
|
||||
@@ -130,6 +124,7 @@ class MultipartHelper extends AbstractHelper
|
||||
$contPart = $part;
|
||||
$part = $part->getParent();
|
||||
} while ($part !== $alternativePart);
|
||||
\assert($contPart instanceof IMimePart);
|
||||
return $contPart;
|
||||
}
|
||||
|
||||
@@ -156,7 +151,7 @@ class MultipartHelper extends AbstractHelper
|
||||
$alternativePart = $message->getPart(0, PartFilter::fromInlineContentType('multipart/alternative'));
|
||||
}
|
||||
$message->removePart($rmPart);
|
||||
if ($alternativePart !== null && $alternativePart instanceof IMultiPart) {
|
||||
if ($alternativePart instanceof IMultiPart) {
|
||||
if ($alternativePart->getChildCount() === 1) {
|
||||
$this->genericHelper->replacePart($message, $alternativePart, $alternativePart->getChild(0));
|
||||
} elseif ($alternativePart->getChildCount() === 0) {
|
||||
@@ -250,15 +245,15 @@ class MultipartHelper extends AbstractHelper
|
||||
* If the passed $mimeType is text/plain, searches for a text/html part.
|
||||
* Otherwise searches for a text/plain part to return.
|
||||
*
|
||||
* @return IMimePart or null if not found
|
||||
* @return IMessagePart or null if not found
|
||||
*/
|
||||
public function findOtherContentPartFor(IMessage $message, string $mimeType) : ?IMimePart
|
||||
public function findOtherContentPartFor(IMessage $message, string $mimeType) : ?IMessagePart
|
||||
{
|
||||
$altPart = $message->getPart(
|
||||
0,
|
||||
PartFilter::fromInlineContentType(($mimeType === 'text/plain') ? 'text/html' : 'text/plain')
|
||||
);
|
||||
if ($altPart !== null && $altPart->getParent() !== null && $altPart->getParent()->isMultiPart()) {
|
||||
if ($altPart?->getParent()?->isMultiPart()) {
|
||||
$altPartParent = $altPart->getParent();
|
||||
if ($altPartParent->getChildCount(PartFilter::fromDisposition('inline')) !== 1) {
|
||||
$altPart = $this->createMultipartRelatedPartForInlineChildrenOf($altPartParent);
|
||||
@@ -345,6 +340,7 @@ class MultipartHelper extends AbstractHelper
|
||||
{
|
||||
$alt = $message->getPart(0, PartFilter::fromInlineContentType('multipart/alternative'));
|
||||
if ($alt !== null) {
|
||||
\assert($alt instanceof IMimePart);
|
||||
return $this->removeAllContentPartsFromAlternative($message, $mimeType, $alt, $keepOtherContent);
|
||||
}
|
||||
$message->removeAllParts(PartFilter::fromInlineContentType($mimeType));
|
||||
@@ -361,14 +357,14 @@ class MultipartHelper extends AbstractHelper
|
||||
{
|
||||
$parts = $message->getAllParts(PartFilter::fromInlineContentType($mimeType));
|
||||
$alt = $message->getPart(0, PartFilter::fromInlineContentType('multipart/alternative'));
|
||||
if ($parts === null || !isset($parts[$index])) {
|
||||
if (!isset($parts[$index])) {
|
||||
return false;
|
||||
} elseif (\count($parts) === 1) {
|
||||
return $this->removeAllContentPartsByMimeType($message, $mimeType, true);
|
||||
}
|
||||
$part = $parts[$index];
|
||||
$message->removePart($part);
|
||||
if ($alt !== null && $alt->getChildCount() === 1) {
|
||||
if ($alt?->getChildCount() === 1) {
|
||||
$this->genericHelper->replacePart($message, $alt, $alt->getChild(0));
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -21,25 +21,13 @@ use ZBateson\MailMimeParser\Message\IMessagePart;
|
||||
*/
|
||||
class PrivacyHelper extends AbstractHelper
|
||||
{
|
||||
/**
|
||||
* @var GenericHelper a GenericHelper instance
|
||||
*/
|
||||
private GenericHelper $genericHelper;
|
||||
|
||||
/**
|
||||
* @var MultipartHelper a MultipartHelper instance
|
||||
*/
|
||||
private MultipartHelper $multipartHelper;
|
||||
|
||||
public function __construct(
|
||||
IMimePartFactory $mimePartFactory,
|
||||
IUUEncodedPartFactory $uuEncodedPartFactory,
|
||||
GenericHelper $genericHelper,
|
||||
MultipartHelper $multipartHelper
|
||||
private readonly GenericHelper $genericHelper,
|
||||
private readonly MultipartHelper $multipartHelper
|
||||
) {
|
||||
parent::__construct($mimePartFactory, $uuEncodedPartFactory);
|
||||
$this->genericHelper = $genericHelper;
|
||||
$this->multipartHelper = $multipartHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,9 +87,7 @@ class PrivacyHelper extends AbstractHelper
|
||||
*/
|
||||
public function overwrite8bitContentEncoding(IMessage $message) : static
|
||||
{
|
||||
$parts = $message->getAllParts(function(IMessagePart $part) {
|
||||
return \strcasecmp($part->getContentTransferEncoding(), '8bit') === 0;
|
||||
});
|
||||
$parts = $message->getAllParts(fn(IMessagePart $part) => \strcasecmp($part->getContentTransferEncoding(), '8bit') === 0);
|
||||
foreach ($parts as $part) {
|
||||
$contentType = \strtolower($part->getContentType());
|
||||
$part->setRawHeader(
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
namespace ZBateson\MailMimeParser\Message;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use SplSubject;
|
||||
use ZBateson\MailMimeParser\IErrorBag;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
|
||||
@@ -19,12 +18,9 @@ use ZBateson\MailMimeParser\MailMimeParser;
|
||||
* contain content, have a parent, and identify the type of content (e.g.
|
||||
* mime-type or charset) agnostically.
|
||||
*
|
||||
* The interface extends SplSubject -- any modifications to a message must
|
||||
* notify any attached observers.
|
||||
*
|
||||
* @author Zaahid Bateson
|
||||
*/
|
||||
interface IMessagePart extends IErrorBag, SplSubject
|
||||
interface IMessagePart extends IErrorBag
|
||||
{
|
||||
/**
|
||||
* Returns this part's parent.
|
||||
@@ -47,10 +43,10 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
* Returns the mime type of the content, or $default if one is not set.
|
||||
*
|
||||
* @param string $default Optional override for the default return value of
|
||||
* 'text/plain.
|
||||
* 'text/plain'.
|
||||
* @return string the mime type
|
||||
*/
|
||||
public function getContentType(string $default = 'text/plain') : ?string;
|
||||
public function getContentType(string $default = 'text/plain') : string;
|
||||
|
||||
/**
|
||||
* Returns the charset of the content, or null if not applicable/defined.
|
||||
@@ -127,7 +123,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
* doesn't have a content section.
|
||||
*
|
||||
* To get a stream without charset conversion if you know the part's content
|
||||
* contains a binary stream, call {@see self::getBinaryContentStream()}
|
||||
* contains a binary stream, call {@see IMessagePart::getBinaryContentStream()}
|
||||
* instead.
|
||||
*
|
||||
* The library automatically handles decoding and charset conversion (to the
|
||||
@@ -282,7 +278,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
*
|
||||
* If the part has not been modified and represents a parsed part, the
|
||||
* original stream should be returned. Otherwise a stream representation of
|
||||
* the part including its modifications should be returned. This insures
|
||||
* the part including its modifications should be returned. This ensures
|
||||
* that an unmodified, signed message could be passed on that way even after
|
||||
* parsing and reading.
|
||||
*
|
||||
@@ -299,7 +295,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
* resource handle.
|
||||
* @see IMessagePart::__toString() to write the part to a string and return
|
||||
* it.
|
||||
* @see IMessage::save() to write the part to a file, resource handle or
|
||||
* @see IMessagePart::save() to write the part to a file, resource handle or
|
||||
* Psr7 stream.
|
||||
* @return resource the resource handle containing the part.
|
||||
*/
|
||||
@@ -311,7 +307,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
*
|
||||
* If the part has not been modified and represents a parsed part, the
|
||||
* original stream should be returned. Otherwise a stream representation of
|
||||
* the part including its modifications should be returned. This insures
|
||||
* the part including its modifications should be returned. This ensures
|
||||
* that an unmodified, signed message could be passed on that way even after
|
||||
* parsing and reading.
|
||||
*
|
||||
@@ -327,7 +323,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
* @see IMessagePart::getResourceHandle() to get a resource handle.
|
||||
* @see IMessagePart::__toString() to write the part to a string and return
|
||||
* it.
|
||||
* @see IMessage::save() to write the part to a file, resource handle or
|
||||
* @see IMessagePart::save() to write the part to a file, resource handle or
|
||||
* Psr7 stream.
|
||||
* @return StreamInterface the stream containing the part.
|
||||
*/
|
||||
@@ -340,7 +336,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
* If the part has not been modified and represents a parsed part, the
|
||||
* original stream should be written to the file. Otherwise a stream
|
||||
* representation of the part including its modifications should be written.
|
||||
* This insures that an unmodified, signed message could be passed on this
|
||||
* This ensures that an unmodified, signed message could be passed on this
|
||||
* way even after parsing and reading.
|
||||
*
|
||||
* The written stream is not guaranteed to be RFC822 (or greater) compliant
|
||||
@@ -360,7 +356,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
*
|
||||
* @see IMessagePart::getResourceHandle() to get a resource handle.
|
||||
* @see IMessagePart::__toString() to get the part in a string.
|
||||
* @see IMessage::save() to write the part to a file, resource handle or
|
||||
* @see IMessagePart::save() to write the part to a file, resource handle or
|
||||
* Psr7 stream.
|
||||
* @param string|resource|StreamInterface $filenameResourceOrStream the
|
||||
* file, resource, or stream to write to.
|
||||
@@ -379,7 +375,7 @@ interface IMessagePart extends IErrorBag, SplSubject
|
||||
* @see IMessagePart::getStream() to get a Psr7 StreamInterface instead of a
|
||||
* string.
|
||||
* @see IMessagePart::getResourceHandle() to get a resource handle.
|
||||
* @see IMessage::save() to write the part to a file, resource handle or
|
||||
* @see IMessagePart::save() to write the part to a file, resource handle or
|
||||
* Psr7 stream.
|
||||
*/
|
||||
public function __toString() : string;
|
||||
|
||||
@@ -36,7 +36,8 @@ interface IMimePart extends IMultiPart
|
||||
* same name, the one at the passed offset is returned.
|
||||
*
|
||||
* Note that mime header names aren't case sensitive, and the '-' character
|
||||
* is ignored, so ret
|
||||
* is ignored, so 'Content-Type', 'content-type', and 'ContentType' are
|
||||
* equivalent.
|
||||
*
|
||||
* If a header with the given $name and $offset doesn't exist, null is
|
||||
* returned.
|
||||
@@ -58,7 +59,10 @@ interface IMimePart extends IMultiPart
|
||||
* @param string $name The name of the header to retrieve.
|
||||
* @param int $offset Optional offset if there are multiple headers with the
|
||||
* given name.
|
||||
* @return ?IHeader the header object if it exists, or null if not
|
||||
* @return ?IHeader The concrete header type depends on the header name
|
||||
* (AddressHeader, DateHeader, GenericHeader, IdHeader,
|
||||
* ParameterHeader, ReceivedHeader, or SubjectHeader). See
|
||||
* {@see HeaderFactory} for the mapping.
|
||||
*/
|
||||
public function getHeader(string $name, int $offset = 0) : ?IHeader;
|
||||
|
||||
@@ -70,6 +74,8 @@ interface IMimePart extends IMultiPart
|
||||
* same name, the one at the passed offset is returned.
|
||||
*
|
||||
* Note that mime headers aren't case sensitive, and the '-' character is
|
||||
* ignored, so 'Content-Type', 'content-type', and 'ContentType' are
|
||||
* equivalent.
|
||||
*
|
||||
* If a header with the given $name and $offset doesn't exist, null is
|
||||
* returned.
|
||||
|
||||
@@ -184,7 +184,7 @@ interface IMultiPart extends IMessagePart
|
||||
* this part.
|
||||
* @see IMultiPart::getAllParts() to get an array of all parts with an
|
||||
* optional filter.
|
||||
* @return RecursiveIterator<IMessagePart>
|
||||
* @return RecursiveIterator<int, IMessagePart>
|
||||
*/
|
||||
public function getChildIterator() : RecursiveIterator;
|
||||
|
||||
@@ -250,18 +250,16 @@ interface IMultiPart extends IMessagePart
|
||||
* If the $position parameter is non-null, adds the part at the passed
|
||||
* position index, otherwise adds it as the last child.
|
||||
*
|
||||
* @param MessagePart $part The part to add.
|
||||
* @param IMessagePart $part The part to add.
|
||||
* @param int $position Optional insertion position 0-based index.
|
||||
*/
|
||||
public function addChild(MessagePart $part, ?int $position = null) : static;
|
||||
public function addChild(IMessagePart $part, ?int $position = null) : static;
|
||||
|
||||
/**
|
||||
* Removes the child part from this part and returns its previous position
|
||||
* or null if it wasn't found.
|
||||
* Removes the child part from this part.
|
||||
*
|
||||
* Note that if the part is not a direct child of this part, the returned
|
||||
* position is its index within its parent (calls removePart on its direct
|
||||
* parent).
|
||||
* Note that if the part is not a direct child of this part, it is removed
|
||||
* from its direct parent instead.
|
||||
*
|
||||
* This also means that parts from unrelated parts/messages could be removed
|
||||
* by a call to removePart -- it will always remove the part from its parent
|
||||
@@ -269,10 +267,8 @@ interface IMultiPart extends IMessagePart
|
||||
* ```php $part->getParent()->removePart(); ```.
|
||||
*
|
||||
* @param IMessagePart $part The part to remove
|
||||
* @return int|null The previous index position of the part within its old
|
||||
* parent.
|
||||
*/
|
||||
public function removePart(IMessagePart $part) : ?int;
|
||||
public function removePart(IMessagePart $part) : static;
|
||||
|
||||
/**
|
||||
* Removes all parts below the current part. If a callable filter is
|
||||
|
||||
@@ -13,27 +13,17 @@ use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SplObjectStorage;
|
||||
use SplObserver;
|
||||
use SplSubject;
|
||||
use ZBateson\MailMimeParser\ErrorBag;
|
||||
use ZBateson\MailMimeParser\MailMimeParser;
|
||||
use ZBateson\MailMimeParser\Stream\MessagePartStreamDecorator;
|
||||
|
||||
/**
|
||||
* Most basic representation of a single part of an email.
|
||||
*
|
||||
* @author Zaahid Bateson
|
||||
*/
|
||||
abstract class MessagePart extends ErrorBag implements IMessagePart
|
||||
abstract class MessagePart extends ErrorBag implements IMessagePart, SplSubject
|
||||
{
|
||||
/**
|
||||
* @var ?IMimePart parent part
|
||||
*/
|
||||
protected ?IMimePart $parent;
|
||||
|
||||
/**
|
||||
* @var PartStreamContainer holds 'stream' and 'content stream'.
|
||||
*/
|
||||
protected PartStreamContainer $partStreamContainer;
|
||||
|
||||
/**
|
||||
* @var ?string can be used to set an override for content's charset in cases
|
||||
* where a user knows the charset on the content is not what it claims
|
||||
@@ -49,19 +39,17 @@ abstract class MessagePart extends ErrorBag implements IMessagePart
|
||||
protected bool $ignoreTransferEncoding = false;
|
||||
|
||||
/**
|
||||
* @var SplObjectStorage attached observers that need to be notified of
|
||||
* @var SplObjectStorage<SplObserver, null> attached observers that need to be notified of
|
||||
* modifications to this part.
|
||||
*/
|
||||
protected SplObjectStorage $observers;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
PartStreamContainer $streamContainer,
|
||||
?IMimePart $parent = null
|
||||
protected readonly PartStreamContainer $partStreamContainer,
|
||||
protected ?IMimePart $parent = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
$this->partStreamContainer = $streamContainer;
|
||||
$this->parent = $parent;
|
||||
$this->observers = new SplObjectStorage();
|
||||
}
|
||||
|
||||
@@ -108,7 +96,7 @@ abstract class MessagePart extends ErrorBag implements IMessagePart
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContentStream(string $charset = MailMimeParser::DEFAULT_CHARSET) : ?MessagePartStreamDecorator
|
||||
public function getContentStream(string $charset = MailMimeParser::DEFAULT_CHARSET) : ?StreamInterface
|
||||
{
|
||||
if ($this->hasContent()) {
|
||||
$tr = ($this->ignoreTransferEncoding) ? '' : $this->getContentTransferEncoding();
|
||||
@@ -123,7 +111,7 @@ abstract class MessagePart extends ErrorBag implements IMessagePart
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getBinaryContentStream() : ?MessagePartStreamDecorator
|
||||
public function getBinaryContentStream() : ?StreamInterface
|
||||
{
|
||||
if ($this->hasContent()) {
|
||||
$tr = ($this->ignoreTransferEncoding) ? '' : $this->getContentTransferEncoding();
|
||||
@@ -188,10 +176,10 @@ abstract class MessagePart extends ErrorBag implements IMessagePart
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setContent($resource, string $charset = MailMimeParser::DEFAULT_CHARSET) : static
|
||||
public function setContent($resource, string $resourceCharset = MailMimeParser::DEFAULT_CHARSET) : static
|
||||
{
|
||||
$stream = Utils::streamFor($resource);
|
||||
$this->attachContentStream($stream, $charset);
|
||||
$this->attachContentStream($stream, $resourceCharset);
|
||||
// this->notify() called in attachContentStream
|
||||
return $this;
|
||||
}
|
||||
@@ -240,7 +228,8 @@ abstract class MessagePart extends ErrorBag implements IMessagePart
|
||||
}
|
||||
$params .= ', content-type=' . $this->getContentType();
|
||||
$nsClass = static::class;
|
||||
$class = \substr($nsClass, (\strrpos($nsClass, '\\') ?? -1) + 1);
|
||||
$pos = \strrpos($nsClass, '\\');
|
||||
$class = ($pos !== false) ? \substr($nsClass, $pos + 1) : $nsClass;
|
||||
return $class . '(' . \spl_object_id($this) . $params . ')';
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,18 @@ class MimePart extends MultiPart implements IMimePart
|
||||
*/
|
||||
protected PartHeaderContainer $headerContainer;
|
||||
|
||||
/**
|
||||
* @var string Fallback charset for text parts without a declared charset.
|
||||
*/
|
||||
protected string $fallbackCharset;
|
||||
|
||||
public function __construct(
|
||||
?IMimePart $parent = null,
|
||||
?LoggerInterface $logger = null,
|
||||
?PartStreamContainer $streamContainer = null,
|
||||
?PartHeaderContainer $headerContainer = null,
|
||||
?PartChildrenContainer $partChildrenContainer = null
|
||||
?PartChildrenContainer $partChildrenContainer = null,
|
||||
string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
@@ -44,6 +50,7 @@ class MimePart extends MultiPart implements IMimePart
|
||||
$parent
|
||||
);
|
||||
$this->headerContainer = $headerContainer ?? $di->get(PartHeaderContainer::class);
|
||||
$this->fallbackCharset = $defaultFallbackCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +122,7 @@ class MimePart extends MultiPart implements IMimePart
|
||||
* than text/plain if needed.
|
||||
* @return string the mime type
|
||||
*/
|
||||
public function getContentType(string $default = 'text/plain') : ?string
|
||||
public function getContentType(string $default = 'text/plain') : string
|
||||
{
|
||||
return \strtolower($this->getHeaderValue(HeaderConsts::CONTENT_TYPE, $default));
|
||||
}
|
||||
@@ -137,7 +144,7 @@ class MimePart extends MultiPart implements IMimePart
|
||||
if ($charset === null || \strcasecmp($charset, 'binary') === 0) {
|
||||
$contentType = $this->getContentType();
|
||||
if ($contentType === 'text/plain' || $contentType === 'text/html') {
|
||||
return 'ISO-8859-1';
|
||||
return $this->fallbackCharset;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -21,19 +21,13 @@ use RecursiveIteratorIterator;
|
||||
*/
|
||||
abstract class MultiPart extends MessagePart implements IMultiPart
|
||||
{
|
||||
/**
|
||||
* @var PartChildrenContainer child part container
|
||||
*/
|
||||
protected PartChildrenContainer $partChildrenContainer;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
PartStreamContainer $streamContainer,
|
||||
PartChildrenContainer $partChildrenContainer,
|
||||
protected readonly PartChildrenContainer $partChildrenContainer,
|
||||
?IMimePart $parent = null
|
||||
) {
|
||||
parent::__construct($logger, $streamContainer, $parent);
|
||||
$this->partChildrenContainer = $partChildrenContainer;
|
||||
}
|
||||
|
||||
private function getAllPartsIterator() : AppendIterator
|
||||
@@ -90,6 +84,9 @@ abstract class MultiPart extends MessagePart implements IMultiPart
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecursiveIterator<int, IMessagePart>
|
||||
*/
|
||||
public function getChildIterator() : RecursiveIterator
|
||||
{
|
||||
return $this->partChildrenContainer;
|
||||
@@ -133,9 +130,10 @@ abstract class MultiPart extends MessagePart implements IMultiPart
|
||||
});
|
||||
}
|
||||
|
||||
public function addChild(MessagePart $part, ?int $position = null) : static
|
||||
public function addChild(IMessagePart $part, ?int $position = null) : static
|
||||
{
|
||||
if ($part !== $this) {
|
||||
\assert($part instanceof MessagePart);
|
||||
$part->parent = $this;
|
||||
$this->partChildrenContainer->add($part, $position);
|
||||
$this->notify();
|
||||
@@ -143,18 +141,19 @@ abstract class MultiPart extends MessagePart implements IMultiPart
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removePart(IMessagePart $part) : ?int
|
||||
public function removePart(IMessagePart $part) : static
|
||||
{
|
||||
$parent = $part->getParent();
|
||||
if ($this !== $parent && $parent !== null) {
|
||||
return $parent->removePart($part);
|
||||
$parent->removePart($part);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$position = $this->partChildrenContainer->remove($part);
|
||||
if ($position !== null) {
|
||||
$this->notify();
|
||||
}
|
||||
return $position;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAllParts(?callable $fnFilter = null) : int
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
namespace ZBateson\MailMimeParser\Message;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Represents part of a non-mime message.
|
||||
*
|
||||
@@ -14,6 +16,17 @@ namespace ZBateson\MailMimeParser\Message;
|
||||
*/
|
||||
abstract class NonMimePart extends MessagePart
|
||||
{
|
||||
protected string $fallbackCharset = 'ISO-8859-1';
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
PartStreamContainer $partStreamContainer,
|
||||
?IMimePart $parent = null,
|
||||
string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
parent::__construct($logger, $partStreamContainer, $parent);
|
||||
$this->fallbackCharset = $defaultFallbackCharset;
|
||||
}
|
||||
/**
|
||||
* Returns true.
|
||||
*
|
||||
@@ -26,17 +39,17 @@ abstract class NonMimePart extends MessagePart
|
||||
/**
|
||||
* Returns text/plain
|
||||
*/
|
||||
public function getContentType(string $default = 'text/plain') : ?string
|
||||
public function getContentType(string $default = 'text/plain') : string
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ISO-8859-1
|
||||
* Returns the configured fallback charset (ISO-8859-1 by default).
|
||||
*/
|
||||
public function getCharset() : ?string
|
||||
{
|
||||
return 'ISO-8859-1';
|
||||
return $this->fallbackCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,27 +14,23 @@ use RecursiveIterator;
|
||||
/**
|
||||
* Container of IMessagePart items for a parent IMultiPart.
|
||||
*
|
||||
* @implements ArrayAccess<int, IMessagePart>
|
||||
* @implements RecursiveIterator<int, IMessagePart>
|
||||
*
|
||||
* @author Zaahid Bateson
|
||||
*/
|
||||
class PartChildrenContainer implements ArrayAccess, RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* @var IMessagePart[] array of child parts of the IMultiPart object that is
|
||||
* holding this container.
|
||||
*/
|
||||
protected array $children;
|
||||
|
||||
/**
|
||||
* @var int current key position within $children for iteration.
|
||||
*/
|
||||
protected $position = 0;
|
||||
protected int $position = 0;
|
||||
|
||||
/**
|
||||
* @param IMessagePart[] $children
|
||||
*/
|
||||
public function __construct(array $children = [])
|
||||
public function __construct(protected array $children = [])
|
||||
{
|
||||
$this->children = $children;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +46,7 @@ class PartChildrenContainer implements ArrayAccess, RecursiveIterator
|
||||
* If the current element points to an IMultiPart, its child iterator is
|
||||
* returned by calling {@see IMultiPart::getChildIterator()}.
|
||||
*
|
||||
* @return RecursiveIterator<IMessagePart>|null the iterator
|
||||
* @return RecursiveIterator<int, IMessagePart>|null the iterator
|
||||
*/
|
||||
public function getChildren() : ?RecursiveIterator
|
||||
{
|
||||
@@ -98,7 +94,7 @@ class PartChildrenContainer implements ArrayAccess, RecursiveIterator
|
||||
* @param int $position An optional index position (0-based) to add the
|
||||
* child at.
|
||||
*/
|
||||
public function add(IMessagePart $part, $position = null) : static
|
||||
public function add(IMessagePart $part, ?int $position = null) : static
|
||||
{
|
||||
if ($position === null || $position >= \count($this->children)) {
|
||||
$this->children[] = $part;
|
||||
@@ -147,7 +143,7 @@ class PartChildrenContainer implements ArrayAccess, RecursiveIterator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
* @param int|null $offset
|
||||
* @param IMessagePart $value
|
||||
*/
|
||||
public function offsetSet(mixed $offset, mixed $value) : void
|
||||
@@ -167,7 +163,7 @@ class PartChildrenContainer implements ArrayAccess, RecursiveIterator
|
||||
/**
|
||||
* @param int $offset
|
||||
*/
|
||||
public function offsetUnset($offset) : void
|
||||
public function offsetUnset(mixed $offset) : void
|
||||
{
|
||||
\array_splice($this->children, $offset, 1);
|
||||
if ($this->position >= $offset) {
|
||||
|
||||
@@ -13,8 +13,12 @@ namespace ZBateson\MailMimeParser\Message;
|
||||
*
|
||||
* @author Zaahid Bateson
|
||||
*/
|
||||
abstract class PartFilter
|
||||
final class PartFilter
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an 'attachment' filter used by Message::getAttachmentPart.
|
||||
*
|
||||
@@ -70,9 +74,7 @@ abstract class PartFilter
|
||||
*/
|
||||
public static function fromContentType(string $mimeType) : callable
|
||||
{
|
||||
return function(IMessagePart $part) use ($mimeType) {
|
||||
return \strcasecmp($part->getContentType() ?: '', $mimeType) === 0;
|
||||
};
|
||||
return fn(IMessagePart $part) => \strcasecmp($part->getContentType(), $mimeType) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +87,7 @@ abstract class PartFilter
|
||||
{
|
||||
return function(IMessagePart $part) use ($mimeType) {
|
||||
$disp = $part->getContentDisposition();
|
||||
return (\strcasecmp($part->getContentType() ?: '', $mimeType) === 0) && ($disp === null
|
||||
return (\strcasecmp($part->getContentType(), $mimeType) === 0) && ($disp === null
|
||||
|| \strcasecmp($disp, 'attachment') !== 0);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use ZBateson\MailMimeParser\Header\IHeader;
|
||||
/**
|
||||
* Maintains a collection of headers for a part.
|
||||
*
|
||||
* @implements IteratorAggregate<int, array<string>>
|
||||
*
|
||||
* @author Zaahid Bateson
|
||||
*/
|
||||
class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
@@ -25,36 +27,36 @@ class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
/**
|
||||
* @var HeaderFactory the HeaderFactory object used for created headers
|
||||
*/
|
||||
protected $headerFactory;
|
||||
protected HeaderFactory $headerFactory;
|
||||
|
||||
/**
|
||||
* @var string[][] Each element in the array is an array with its first
|
||||
* element set to the header's name, and the second its value.
|
||||
*/
|
||||
private $headers = [];
|
||||
private array $headers = [];
|
||||
|
||||
/**
|
||||
* @var \ZBateson\MailMimeParser\Header\IHeader[] Each element is an IHeader
|
||||
* @var array<int, ?IHeader> Each element is an IHeader
|
||||
* representing the header at the same index in the $headers array. If
|
||||
* an IHeader has not been constructed for the header at that index,
|
||||
* the element would be set to null.
|
||||
*/
|
||||
private $headerObjects = [];
|
||||
private array $headerObjects = [];
|
||||
|
||||
/**
|
||||
* @var array Maps header names by their "normalized" (lower-cased,
|
||||
* @var array<string, int[]> Maps header names by their "normalized" (lower-cased,
|
||||
* non-alphanumeric characters stripped) name to an array of indexes in
|
||||
* the $headers array. For example:
|
||||
* $headerMap['contenttype'] = [ 1, 4 ]
|
||||
* would indicate that the headers in $headers[1] and $headers[4] are
|
||||
* both headers with the name 'Content-Type' or 'contENTtype'.
|
||||
*/
|
||||
private $headerMap = [];
|
||||
private array $headerMap = [];
|
||||
|
||||
/**
|
||||
* @var int the next index to use for $headers and $headerObjects.
|
||||
*/
|
||||
private $nextIndex = 0;
|
||||
private int $nextIndex = 0;
|
||||
|
||||
/**
|
||||
* Pass a PartHeaderContainer as the second parameter. This is useful when
|
||||
@@ -103,10 +105,7 @@ class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
{
|
||||
$s = $this->headerFactory->getNormalizedHeaderName($name);
|
||||
if (isset($this->headerMap[$s])) {
|
||||
$self = $this;
|
||||
$filtered = \array_filter($this->headerMap[$s], function($h) use ($name, $self) {
|
||||
return (\strcasecmp($self->headers[$h][0], $name) === 0);
|
||||
});
|
||||
$filtered = \array_filter($this->headerMap[$s], fn($h) => \strcasecmp($this->headers[$h][0], $name) === 0);
|
||||
return (!empty($filtered)) ? $filtered : $this->headerMap[$s];
|
||||
}
|
||||
return null;
|
||||
@@ -157,10 +156,7 @@ class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
{
|
||||
$a = $this->getAllWithOriginalHeaderNameIfSet($name);
|
||||
if (!empty($a)) {
|
||||
$self = $this;
|
||||
return \array_map(function($index) use ($self) {
|
||||
return $self->getByIndex($index);
|
||||
}, $a);
|
||||
return \array_map(fn($index) => $this->getByIndex($index), $a);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
@@ -284,7 +280,7 @@ class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
*/
|
||||
public function getHeaderObjects() : array
|
||||
{
|
||||
return \array_filter(\array_map([$this, 'getByIndex'], \array_keys($this->headers)));
|
||||
return \array_filter(\array_map($this->getByIndex(...), \array_keys($this->headers)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,7 +308,7 @@ class PartHeaderContainer extends ErrorBag implements IteratorAggregate
|
||||
*
|
||||
* [ 'Header-Name', 'Header Value' ]
|
||||
*
|
||||
* return Traversable<array<string>>
|
||||
* @return Traversable<int, array<string>>
|
||||
*/
|
||||
public function getIterator() : Traversable
|
||||
{
|
||||
|
||||
@@ -12,7 +12,6 @@ use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use ZBateson\MailMimeParser\ErrorBag;
|
||||
use ZBateson\MailMimeParser\Stream\MessagePartStreamDecorator;
|
||||
use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
use ZBateson\MbWrapper\UnsupportedCharsetException;
|
||||
@@ -35,29 +34,10 @@ use ZBateson\MbWrapper\UnsupportedCharsetException;
|
||||
class PartStreamContainer extends ErrorBag
|
||||
{
|
||||
/**
|
||||
* @var MbWrapper to test charsets and see if they're supported.
|
||||
* @var StreamInterface stream containing the part's headers, content and
|
||||
* children
|
||||
*/
|
||||
protected MbWrapper $mbWrapper;
|
||||
|
||||
/**
|
||||
* @var bool if false, reading from a content stream with an unsupported
|
||||
* charset will be tried with the default charset, otherwise the stream
|
||||
* created with the unsupported charset, and an exception will be
|
||||
* thrown when read from.
|
||||
*/
|
||||
protected bool $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
|
||||
/**
|
||||
* @var StreamFactory used to apply psr7 stream decorators to the
|
||||
* attached StreamInterface based on encoding.
|
||||
*/
|
||||
protected StreamFactory $streamFactory;
|
||||
|
||||
/**
|
||||
* @var MessagePartStreamDecorator stream containing the part's headers,
|
||||
* content and children wrapped in a MessagePartStreamDecorator
|
||||
*/
|
||||
protected MessagePartStreamDecorator $stream;
|
||||
protected StreamInterface $stream;
|
||||
|
||||
/**
|
||||
* @var StreamInterface a stream containing this part's content
|
||||
@@ -82,7 +62,7 @@ class PartStreamContainer extends ErrorBag
|
||||
protected bool $detachParsedStream = false;
|
||||
|
||||
/**
|
||||
* @var array<string, null> map of the active encoding filter on the current handle.
|
||||
* @var array<string, ?string> map of the active encoding filter on the current handle.
|
||||
*/
|
||||
private array $encoding = [
|
||||
'type' => null,
|
||||
@@ -90,7 +70,7 @@ class PartStreamContainer extends ErrorBag
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, null> map of the active charset filter on the current handle.
|
||||
* @var array<string, ?string> map of the active charset filter on the current handle.
|
||||
*/
|
||||
private array $charset = [
|
||||
'from' => null,
|
||||
@@ -100,21 +80,18 @@ class PartStreamContainer extends ErrorBag
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $streamFactory,
|
||||
MbWrapper $mbWrapper,
|
||||
bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
protected readonly StreamFactory $streamFactory,
|
||||
protected readonly MbWrapper $mbWrapper,
|
||||
protected readonly bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->mbWrapper = $mbWrapper;
|
||||
$this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the part's stream containing the part's headers, content, and
|
||||
* children.
|
||||
*/
|
||||
public function setStream(MessagePartStreamDecorator $stream) : static
|
||||
public function setStream(StreamInterface $stream) : static
|
||||
{
|
||||
$this->stream = $stream;
|
||||
return $this;
|
||||
@@ -124,7 +101,7 @@ class PartStreamContainer extends ErrorBag
|
||||
* Returns the part's stream containing the part's headers, content, and
|
||||
* children.
|
||||
*/
|
||||
public function getStream() : MessagePartStreamDecorator
|
||||
public function getStream() : StreamInterface
|
||||
{
|
||||
// error out if called before setStream, getStream should never return
|
||||
// null.
|
||||
@@ -285,7 +262,7 @@ class PartStreamContainer extends ErrorBag
|
||||
?string $transferEncoding,
|
||||
?string $fromCharset,
|
||||
?string $toCharset
|
||||
) : ?MessagePartStreamDecorator {
|
||||
) : ?StreamInterface {
|
||||
if ($this->contentStream === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -314,7 +291,7 @@ class PartStreamContainer extends ErrorBag
|
||||
* Checks what transfer-encoding decoder stream is attached on the
|
||||
* underlying stream, and resets it if the requested arguments differ.
|
||||
*/
|
||||
public function getBinaryContentStream(IMessagePart $part, ?string $transferEncoding = null) : ?MessagePartStreamDecorator
|
||||
public function getBinaryContentStream(IMessagePart $part, ?string $transferEncoding = null) : ?StreamInterface
|
||||
{
|
||||
if ($this->contentStream === null) {
|
||||
return null;
|
||||
|
||||
@@ -17,31 +17,21 @@ use ZBateson\MailMimeParser\MailMimeParser;
|
||||
*/
|
||||
class UUEncodedPart extends NonMimePart implements IUUEncodedPart
|
||||
{
|
||||
/**
|
||||
* @var int the unix file permission
|
||||
*/
|
||||
protected ?int $mode = null;
|
||||
|
||||
/**
|
||||
* @var string the name of the file in the uuencoding 'header'.
|
||||
*/
|
||||
protected ?string $filename = null;
|
||||
|
||||
public function __construct(
|
||||
?int $mode = null,
|
||||
?string $filename = null,
|
||||
protected ?int $mode = null,
|
||||
protected ?string $filename = null,
|
||||
?IMimePart $parent = null,
|
||||
?LoggerInterface $logger = null,
|
||||
?PartStreamContainer $streamContainer = null
|
||||
?PartStreamContainer $streamContainer = null,
|
||||
string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
$di = MailMimeParser::getGlobalContainer();
|
||||
parent::__construct(
|
||||
$logger ?? $di->get(LoggerInterface::class),
|
||||
$streamContainer ?? $di->get(PartStreamContainer::class),
|
||||
$parent
|
||||
$parent,
|
||||
$defaultFallbackCharset
|
||||
);
|
||||
$this->mode = $mode;
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +67,7 @@ class UUEncodedPart extends NonMimePart implements IUUEncodedPart
|
||||
/**
|
||||
* Returns 'application/octet-stream'.
|
||||
*/
|
||||
public function getContentType(string $default = 'application/octet-stream') : ?string
|
||||
public function getContentType(string $default = 'application/octet-stream') : string
|
||||
{
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
@@ -21,26 +21,6 @@ use ZBateson\MailMimeParser\Parser\Proxy\ParserPartProxyFactory;
|
||||
*/
|
||||
abstract class AbstractParserService implements IParserService
|
||||
{
|
||||
/**
|
||||
* @var ParserPartProxyFactory the parser's message proxy factory service
|
||||
* responsible for creating an IMessage part wrapped in a
|
||||
* ParserPartProxy.
|
||||
*/
|
||||
protected ParserPartProxyFactory $parserMessageProxyFactory;
|
||||
|
||||
/**
|
||||
* @var ParserPartProxyFactory the parser's part proxy factory service
|
||||
* responsible for creating IMessagePart parts wrapped in a
|
||||
* ParserPartProxy.
|
||||
*/
|
||||
protected ParserPartProxyFactory $parserPartProxyFactory;
|
||||
|
||||
/**
|
||||
* @var PartBuilderFactory Service for creating PartBuilder objects for new
|
||||
* children.
|
||||
*/
|
||||
protected PartBuilderFactory $partBuilderFactory;
|
||||
|
||||
/**
|
||||
* @var ParserManagerService the ParserManager, which should call setParserManager
|
||||
* when the parser is added.
|
||||
@@ -48,13 +28,10 @@ abstract class AbstractParserService implements IParserService
|
||||
protected ParserManagerService $parserManager;
|
||||
|
||||
public function __construct(
|
||||
ParserPartProxyFactory $parserMessageProxyFactory,
|
||||
ParserPartProxyFactory $parserPartProxyFactory,
|
||||
PartBuilderFactory $partBuilderFactory
|
||||
protected readonly ParserPartProxyFactory $parserMessageProxyFactory,
|
||||
protected readonly ParserPartProxyFactory $parserPartProxyFactory,
|
||||
protected readonly PartBuilderFactory $partBuilderFactory
|
||||
) {
|
||||
$this->parserMessageProxyFactory = $parserMessageProxyFactory;
|
||||
$this->parserPartProxyFactory = $parserPartProxyFactory;
|
||||
$this->partBuilderFactory = $partBuilderFactory;
|
||||
}
|
||||
|
||||
public function setParserManager(ParserManagerService $pm) : static
|
||||
|
||||
@@ -17,14 +17,10 @@ use ZBateson\MailMimeParser\Message\PartHeaderContainer;
|
||||
*/
|
||||
class HeaderParserService
|
||||
{
|
||||
private int $maxHeaderCount;
|
||||
|
||||
private int $maxHeaderSizeBytes;
|
||||
|
||||
public function __construct(int $maxHeaderCount = 1000, int $maxHeaderSizeBytes = 1048576)
|
||||
{
|
||||
$this->maxHeaderCount = $maxHeaderCount;
|
||||
$this->maxHeaderSizeBytes = $maxHeaderSizeBytes;
|
||||
public function __construct(
|
||||
private readonly int $maxHeaderCount = 1000,
|
||||
private readonly int $maxHeaderSizeBytes = 1048576
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,39 +19,12 @@ use ZBateson\MailMimeParser\Message\Factory\PartHeaderContainerFactory;
|
||||
*/
|
||||
class MessageParserService
|
||||
{
|
||||
/**
|
||||
* @var PartHeaderContainerFactory To create a container to read the
|
||||
* message's headers into.
|
||||
*/
|
||||
protected PartHeaderContainerFactory $partHeaderContainerFactory;
|
||||
|
||||
/**
|
||||
* @var ParserManagerService To figure out what parser is responsible for parsing a
|
||||
* message.
|
||||
*/
|
||||
protected ParserManagerService $parserManager;
|
||||
|
||||
/**
|
||||
* @var PartBuilderFactory To create a PartBuilder representing this
|
||||
* message, and to pass it to ParserManager.
|
||||
*/
|
||||
protected PartBuilderFactory $partBuilderFactory;
|
||||
|
||||
/**
|
||||
* @var HeaderParserService To parse the headers into a PartHeaderContainer.
|
||||
*/
|
||||
protected HeaderParserService $headerParser;
|
||||
|
||||
public function __construct(
|
||||
PartBuilderFactory $pbf,
|
||||
PartHeaderContainerFactory $phcf,
|
||||
ParserManagerService $pm,
|
||||
HeaderParserService $hp
|
||||
protected readonly PartBuilderFactory $partBuilderFactory,
|
||||
protected readonly PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
protected readonly ParserManagerService $parserManager,
|
||||
protected readonly HeaderParserService $headerParser
|
||||
) {
|
||||
$this->partBuilderFactory = $pbf;
|
||||
$this->partHeaderContainerFactory = $phcf;
|
||||
$this->parserManager = $pm;
|
||||
$this->headerParser = $hp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +63,8 @@ class MessageParserService
|
||||
$headerContainer
|
||||
);
|
||||
$proxy = $this->parserManager->createParserProxyFor($partBuilder);
|
||||
return $proxy->getPart();
|
||||
$part = $proxy->getPart();
|
||||
\assert($part instanceof IMessage);
|
||||
return $part;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,34 +22,15 @@ use ZBateson\MailMimeParser\Parser\Proxy\ParserPartProxy;
|
||||
*/
|
||||
class MimeParserService extends AbstractParserService
|
||||
{
|
||||
/**
|
||||
* @var PartHeaderContainerFactory Factory service for creating
|
||||
* PartHeaderContainers for headers.
|
||||
*/
|
||||
protected PartHeaderContainerFactory $partHeaderContainerFactory;
|
||||
|
||||
/**
|
||||
* @var HeaderParserService The HeaderParser service.
|
||||
*/
|
||||
protected HeaderParserService $headerParser;
|
||||
|
||||
/**
|
||||
* @var int Maximum multipart nesting depth.
|
||||
*/
|
||||
protected int $maxMimePartDepth;
|
||||
|
||||
public function __construct(
|
||||
ParserMessageProxyFactory $parserMessageProxyFactory,
|
||||
ParserMimePartProxyFactory $parserMimePartProxyFactory,
|
||||
PartBuilderFactory $partBuilderFactory,
|
||||
PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
HeaderParserService $headerParser,
|
||||
int $maxMimePartDepth = 256
|
||||
protected readonly PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
protected readonly HeaderParserService $headerParser,
|
||||
protected readonly int $maxMimePartDepth = 256
|
||||
) {
|
||||
parent::__construct($parserMessageProxyFactory, $parserMimePartProxyFactory, $partBuilderFactory);
|
||||
$this->partHeaderContainerFactory = $partHeaderContainerFactory;
|
||||
$this->headerParser = $headerParser;
|
||||
$this->maxMimePartDepth = $maxMimePartDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,6 +102,7 @@ class MimeParserService extends AbstractParserService
|
||||
|
||||
public function parseContent(ParserPartProxy $proxy) : static
|
||||
{
|
||||
\assert($proxy instanceof ParserMimePartProxy);
|
||||
$proxy->setStreamContentStartPos($proxy->getMessageResourceHandlePos());
|
||||
$this->findContentBoundary($proxy);
|
||||
return $this;
|
||||
|
||||
@@ -22,16 +22,13 @@ use ZBateson\MailMimeParser\Parser\Proxy\ParserUUEncodedPartProxyFactory;
|
||||
*/
|
||||
class NonMimeParserService extends AbstractParserService
|
||||
{
|
||||
protected UUEncodedPartHeaderContainerFactory $partHeaderContainerFactory;
|
||||
|
||||
public function __construct(
|
||||
ParserNonMimeMessageProxyFactory $parserNonMimeMessageProxyFactory,
|
||||
ParserUUEncodedPartProxyFactory $parserUuEncodedPartProxyFactory,
|
||||
PartBuilderFactory $partBuilderFactory,
|
||||
UUEncodedPartHeaderContainerFactory $uuEncodedPartHeaderContainerFactory
|
||||
protected readonly UUEncodedPartHeaderContainerFactory $partHeaderContainerFactory
|
||||
) {
|
||||
parent::__construct($parserNonMimeMessageProxyFactory, $parserUuEncodedPartProxyFactory, $partBuilderFactory);
|
||||
$this->partHeaderContainerFactory = $uuEncodedPartHeaderContainerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +65,7 @@ class NonMimeParserService extends AbstractParserService
|
||||
*
|
||||
* @param ParserNonMimeMessageProxy|ParserUUEncodedPartProxy $proxy
|
||||
*/
|
||||
private function parseNextPart(ParserPartProxy $proxy) : static
|
||||
private function parseNextPart(ParserNonMimeMessageProxy|ParserUUEncodedPartProxy $proxy) : static
|
||||
{
|
||||
$handle = $proxy->getMessageResourceHandle();
|
||||
while (!\feof($handle)) {
|
||||
@@ -87,6 +84,7 @@ class NonMimeParserService extends AbstractParserService
|
||||
|
||||
public function parseContent(ParserPartProxy $proxy) : static
|
||||
{
|
||||
\assert($proxy instanceof ParserNonMimeMessageProxy || $proxy instanceof ParserUUEncodedPartProxy);
|
||||
$handle = $proxy->getMessageResourceHandle();
|
||||
if ($proxy->getNextPartStart() !== null || \feof($handle)) {
|
||||
return $this;
|
||||
@@ -100,6 +98,7 @@ class NonMimeParserService extends AbstractParserService
|
||||
|
||||
public function parseNextChild(ParserMimePartProxy $proxy) : ?ParserPartProxy
|
||||
{
|
||||
\assert($proxy instanceof ParserNonMimeMessageProxy);
|
||||
$handle = $proxy->getMessageResourceHandle();
|
||||
if ($proxy->getNextPartStart() === null || \feof($handle)) {
|
||||
return null;
|
||||
|
||||
@@ -18,22 +18,15 @@ use ZBateson\MailMimeParser\Parser\Proxy\ParserMimePartProxy;
|
||||
*/
|
||||
class ParserPartChildrenContainer extends PartChildrenContainer
|
||||
{
|
||||
/**
|
||||
* @var ParserMimePartProxy The parser to proxy requests to when trying to
|
||||
* get child parts.
|
||||
*/
|
||||
protected ParserMimePartProxy $parserProxy;
|
||||
|
||||
/**
|
||||
* @var bool Set to true once all parts have been parsed, and requests to
|
||||
* the proxy won't result in any more child parts.
|
||||
*/
|
||||
private bool $allParsed = false;
|
||||
|
||||
public function __construct(ParserMimePartProxy $parserProxy)
|
||||
public function __construct(protected readonly ParserMimePartProxy $parserProxy)
|
||||
{
|
||||
parent::__construct([]);
|
||||
$this->parserProxy = $parserProxy;
|
||||
}
|
||||
|
||||
public function offsetExists($offset) : bool
|
||||
|
||||
@@ -14,7 +14,6 @@ use SplSubject;
|
||||
use ZBateson\MailMimeParser\Message\IMessagePart;
|
||||
use ZBateson\MailMimeParser\Message\PartStreamContainer;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserPartProxy;
|
||||
use ZBateson\MailMimeParser\Stream\MessagePartStreamDecorator;
|
||||
use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
use ZBateson\MbWrapper\MbWrapper;
|
||||
|
||||
@@ -36,16 +35,10 @@ use ZBateson\MbWrapper\MbWrapper;
|
||||
class ParserPartStreamContainer extends PartStreamContainer implements SplObserver
|
||||
{
|
||||
/**
|
||||
* @var ParserPartProxy The parser proxy to ferry requests to on-demand.
|
||||
* @var StreamInterface|null the original stream for a parsed message, used
|
||||
* when the message hasn't changed
|
||||
*/
|
||||
protected ParserPartProxy $parserProxy;
|
||||
|
||||
/**
|
||||
* @var MessagePartStreamDecorator the original stream for a parsed message,
|
||||
* wrapped in a MessagePartStreamDecorator, and used when the message
|
||||
* hasn't changed
|
||||
*/
|
||||
protected ?MessagePartStreamDecorator $parsedStream = null;
|
||||
protected ?StreamInterface $parsedStream = null;
|
||||
|
||||
/**
|
||||
* @var bool set to true if the part's been updated since it was created.
|
||||
@@ -63,10 +56,9 @@ class ParserPartStreamContainer extends PartStreamContainer implements SplObserv
|
||||
StreamFactory $streamFactory,
|
||||
MbWrapper $mbWrapper,
|
||||
bool $throwExceptionReadingPartContentFromUnsupportedCharsets,
|
||||
ParserPartProxy $parserProxy
|
||||
protected readonly ParserPartProxy $parserProxy
|
||||
) {
|
||||
parent::__construct($logger, $streamFactory, $mbWrapper, $throwExceptionReadingPartContentFromUnsupportedCharsets);
|
||||
$this->parserProxy = $parserProxy;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@@ -107,9 +99,7 @@ class ParserPartStreamContainer extends PartStreamContainer implements SplObserv
|
||||
$this->parserProxy
|
||||
)
|
||||
);
|
||||
if ($this->parsedStream !== null) {
|
||||
$this->detachParsedStream = ($this->parsedStream->getMetadata('mmp-detached-stream') === true);
|
||||
}
|
||||
$this->detachParsedStream = ($this->parsedStream->getMetadata('mmp-detached-stream') === true);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@@ -120,13 +110,13 @@ class ParserPartStreamContainer extends PartStreamContainer implements SplObserv
|
||||
return parent::hasContent();
|
||||
}
|
||||
|
||||
public function getContentStream(IMessagePart $part, ?string $transferEncoding, ?string $fromCharset, ?string $toCharset) : ?MessagePartStreamDecorator
|
||||
public function getContentStream(IMessagePart $part, ?string $transferEncoding, ?string $fromCharset, ?string $toCharset) : ?StreamInterface
|
||||
{
|
||||
$this->requestParsedContentStream();
|
||||
return parent::getContentStream($part, $transferEncoding, $fromCharset, $toCharset);
|
||||
}
|
||||
|
||||
public function getBinaryContentStream(IMessagePart $part, ?string $transferEncoding = null) : ?MessagePartStreamDecorator
|
||||
public function getBinaryContentStream(IMessagePart $part, ?string $transferEncoding = null) : ?StreamInterface
|
||||
{
|
||||
$this->requestParsedContentStream();
|
||||
return parent::getBinaryContentStream($part, $transferEncoding);
|
||||
@@ -142,7 +132,7 @@ class ParserPartStreamContainer extends PartStreamContainer implements SplObserv
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStream() : MessagePartStreamDecorator
|
||||
public function getStream() : StreamInterface
|
||||
{
|
||||
$this->requestParsedStream();
|
||||
if (!$this->partUpdated) {
|
||||
|
||||
@@ -19,24 +19,12 @@ use ZBateson\MbWrapper\MbWrapper;
|
||||
*/
|
||||
class ParserPartStreamContainerFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
protected StreamFactory $streamFactory;
|
||||
|
||||
protected MbWrapper $mbWrapper;
|
||||
|
||||
protected bool $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $streamFactory,
|
||||
MbWrapper $mbWrapper,
|
||||
bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly StreamFactory $streamFactory,
|
||||
protected readonly MbWrapper $mbWrapper,
|
||||
protected readonly bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->mbWrapper = $mbWrapper;
|
||||
$this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
}
|
||||
|
||||
public function newInstance(ParserPartProxy $parserProxy) : ParserPartStreamContainer
|
||||
|
||||
@@ -17,22 +17,10 @@ use ZBateson\MailMimeParser\Header\HeaderFactory;
|
||||
*/
|
||||
class UUEncodedPartHeaderContainerFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @var HeaderFactory the HeaderFactory passed to
|
||||
* UUEncodedPartHeaderContainer instances.
|
||||
*/
|
||||
protected HeaderFactory $headerFactory;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger, HeaderFactory $headerFactory)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->headerFactory = $headerFactory;
|
||||
public function __construct(
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly HeaderFactory $headerFactory
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ use Psr\Http\Message\StreamInterface;
|
||||
use ZBateson\MailMimeParser\Header\HeaderConsts;
|
||||
use ZBateson\MailMimeParser\Message\PartHeaderContainer;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserMimePartProxy;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserPartProxy;
|
||||
|
||||
/**
|
||||
* Holds generic/all purpose information about a part while it's being parsed.
|
||||
@@ -67,18 +68,18 @@ class PartBuilder
|
||||
private ?StreamInterface $messageStream = null;
|
||||
|
||||
/**
|
||||
* @var resource the raw message input stream handle constructed from
|
||||
* @var resource|null the raw message input stream handle constructed from
|
||||
* $messageStream or null for a child part
|
||||
*/
|
||||
private mixed $messageHandle = null;
|
||||
|
||||
/**
|
||||
* @var ParserMimePartProxy The parent proxy part if one is set, or null if
|
||||
* @var ParserPartProxy|null The parent proxy part if one is set, or null if
|
||||
* the part being built doesn't have a parent.
|
||||
*/
|
||||
private ?ParserMimePartProxy $parent = null;
|
||||
private ?ParserPartProxy $parent = null;
|
||||
|
||||
public function __construct(PartHeaderContainer $headerContainer, ?StreamInterface $messageStream = null, ?ParserMimePartProxy $parent = null)
|
||||
public function __construct(PartHeaderContainer $headerContainer, ?StreamInterface $messageStream = null, ?ParserPartProxy $parent = null)
|
||||
{
|
||||
$this->headerContainer = $headerContainer;
|
||||
$this->messageStream = $messageStream;
|
||||
@@ -91,7 +92,7 @@ class PartBuilder
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->messageHandle) {
|
||||
if ($this->messageHandle !== null) {
|
||||
\fclose($this->messageHandle);
|
||||
}
|
||||
}
|
||||
@@ -99,7 +100,7 @@ class PartBuilder
|
||||
/**
|
||||
* The ParserPartProxy parent of this PartBuilder.
|
||||
*/
|
||||
public function getParent() : ?ParserMimePartProxy
|
||||
public function getParent() : ?ParserPartProxy
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
@@ -27,22 +27,17 @@ use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
*/
|
||||
class ParserMessageProxyFactory extends ParserMimePartProxyFactory
|
||||
{
|
||||
protected MultipartHelper $multipartHelper;
|
||||
|
||||
protected PrivacyHelper $privacyHelper;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $sdf,
|
||||
PartHeaderContainerFactory $phcf,
|
||||
ParserPartStreamContainerFactory $pscf,
|
||||
ParserPartChildrenContainerFactory $ppccf,
|
||||
MultipartHelper $multipartHelper,
|
||||
PrivacyHelper $privacyHelper
|
||||
StreamFactory $streamFactory,
|
||||
PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
ParserPartStreamContainerFactory $parserPartStreamContainerFactory,
|
||||
ParserPartChildrenContainerFactory $parserPartChildrenContainerFactory,
|
||||
protected readonly MultipartHelper $multipartHelper,
|
||||
protected readonly PrivacyHelper $privacyHelper,
|
||||
string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
parent::__construct($logger, $sdf, $phcf, $pscf, $ppccf);
|
||||
$this->multipartHelper = $multipartHelper;
|
||||
$this->privacyHelper = $privacyHelper;
|
||||
parent::__construct($logger, $streamFactory, $partHeaderContainerFactory, $parserPartStreamContainerFactory, $parserPartChildrenContainerFactory, $defaultFallbackCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +58,8 @@ class ParserMessageProxyFactory extends ParserMimePartProxyFactory
|
||||
$headerContainer,
|
||||
$childrenContainer,
|
||||
$this->multipartHelper,
|
||||
$this->privacyHelper
|
||||
$this->privacyHelper,
|
||||
$this->defaultFallbackCharset
|
||||
);
|
||||
$parserProxy->setPart($message);
|
||||
|
||||
|
||||
@@ -133,7 +133,12 @@ class ParserMimePartProxy extends ParserPartProxy
|
||||
*/
|
||||
public function getContentType() : ?ParameterHeader
|
||||
{
|
||||
return $this->getHeaderContainer()->get(HeaderConsts::CONTENT_TYPE);
|
||||
$header = $this->getHeaderContainer()->get(HeaderConsts::CONTENT_TYPE);
|
||||
if ($header === null) {
|
||||
return null;
|
||||
}
|
||||
\assert($header instanceof ParameterHeader);
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +168,7 @@ class ParserMimePartProxy extends ParserPartProxy
|
||||
public function setEndBoundaryFound(string $line) : bool
|
||||
{
|
||||
$boundary = $this->getMimeBoundary();
|
||||
if ($this->getParent() !== null && $this->getParent()->setEndBoundaryFound($line)) {
|
||||
if ($this->getParent()?->setEndBoundaryFound($line)) {
|
||||
$this->parentBoundaryFound = true;
|
||||
return true;
|
||||
} elseif ($boundary !== null) {
|
||||
@@ -222,7 +227,7 @@ class ParserMimePartProxy extends ParserPartProxy
|
||||
{
|
||||
// check if we're expecting a boundary and didn't find one
|
||||
if (!$this->endBoundaryFound && !$this->parentBoundaryFound) {
|
||||
if (!empty($this->mimeBoundary) || ($this->getParent() !== null && !empty($this->getParent()->mimeBoundary))) {
|
||||
if (!empty($this->mimeBoundary) || !empty($this->getParent()->mimeBoundary)) {
|
||||
$this->addError('End boundary for part not found', LogLevel::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ZBateson\MailMimeParser\Parser\Proxy;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Message\Factory\PartHeaderContainerFactory;
|
||||
use ZBateson\MailMimeParser\Message\IMimePart;
|
||||
use ZBateson\MailMimeParser\Message\MimePart;
|
||||
use ZBateson\MailMimeParser\Parser\IParserService;
|
||||
use ZBateson\MailMimeParser\Parser\Part\ParserPartChildrenContainerFactory;
|
||||
@@ -24,28 +25,14 @@ use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
*/
|
||||
class ParserMimePartProxyFactory extends ParserPartProxyFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
protected StreamFactory $streamFactory;
|
||||
|
||||
protected ParserPartStreamContainerFactory $parserPartStreamContainerFactory;
|
||||
|
||||
protected PartHeaderContainerFactory $partHeaderContainerFactory;
|
||||
|
||||
protected ParserPartChildrenContainerFactory $parserPartChildrenContainerFactory;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $sdf,
|
||||
PartHeaderContainerFactory $phcf,
|
||||
ParserPartStreamContainerFactory $pscf,
|
||||
ParserPartChildrenContainerFactory $ppccf
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly StreamFactory $streamFactory,
|
||||
protected readonly PartHeaderContainerFactory $partHeaderContainerFactory,
|
||||
protected readonly ParserPartStreamContainerFactory $parserPartStreamContainerFactory,
|
||||
protected readonly ParserPartChildrenContainerFactory $parserPartChildrenContainerFactory,
|
||||
protected readonly string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->streamFactory = $sdf;
|
||||
$this->partHeaderContainerFactory = $phcf;
|
||||
$this->parserPartStreamContainerFactory = $pscf;
|
||||
$this->parserPartChildrenContainerFactory = $ppccf;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,12 +48,15 @@ class ParserMimePartProxyFactory extends ParserPartProxyFactory
|
||||
$headerContainer = $this->partHeaderContainerFactory->newInstance($parserProxy->getHeaderContainer());
|
||||
$childrenContainer = $this->parserPartChildrenContainerFactory->newInstance($parserProxy);
|
||||
|
||||
$parent = $partBuilder->getParent()?->getPart();
|
||||
\assert($parent === null || $parent instanceof IMimePart);
|
||||
$part = new MimePart(
|
||||
$partBuilder->getParent()->getPart(),
|
||||
$parent,
|
||||
$this->logger,
|
||||
$streamContainer,
|
||||
$headerContainer,
|
||||
$childrenContainer
|
||||
$childrenContainer,
|
||||
$this->defaultFallbackCharset
|
||||
);
|
||||
$parserProxy->setPart($part);
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ class ParserNonMimeMessageProxyFactory extends ParserMessageProxyFactory
|
||||
$headerContainer,
|
||||
$childrenContainer,
|
||||
$this->multipartHelper,
|
||||
$this->privacyHelper
|
||||
$this->privacyHelper,
|
||||
$this->defaultFallbackCharset
|
||||
);
|
||||
$parserProxy->setPart($message);
|
||||
|
||||
|
||||
@@ -24,25 +24,15 @@ use ZBateson\MailMimeParser\Parser\PartBuilder;
|
||||
*/
|
||||
abstract class ParserPartProxy extends PartBuilder
|
||||
{
|
||||
/**
|
||||
* @var IParserService The parser.
|
||||
*/
|
||||
protected IParserService $parser;
|
||||
|
||||
/**
|
||||
* @var PartBuilder The part's PartBuilder.
|
||||
*/
|
||||
protected PartBuilder $partBuilder;
|
||||
|
||||
/**
|
||||
* @var IMessagePart The part.
|
||||
*/
|
||||
private IMessagePart $part;
|
||||
|
||||
public function __construct(PartBuilder $partBuilder, IParserService $parser)
|
||||
{
|
||||
$this->partBuilder = $partBuilder;
|
||||
$this->parser = $parser;
|
||||
public function __construct(
|
||||
protected readonly PartBuilder $partBuilder,
|
||||
protected readonly IParserService $parser
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +84,7 @@ abstract class ParserPartProxy extends PartBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParent() : ?ParserMimePartProxy
|
||||
public function getParent() : ?ParserPartProxy
|
||||
{
|
||||
return $this->partBuilder->getParent();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ class ParserUUEncodedPartProxy extends ParserPartProxy
|
||||
*/
|
||||
public function getParent() : ParserNonMimeMessageProxy
|
||||
{
|
||||
return parent::getParent();
|
||||
$parent = parent::getParent();
|
||||
\assert($parent instanceof ParserNonMimeMessageProxy);
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace ZBateson\MailMimeParser\Parser\Proxy;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZBateson\MailMimeParser\Message\IMimePart;
|
||||
use ZBateson\MailMimeParser\Message\UUEncodedPart;
|
||||
use ZBateson\MailMimeParser\Parser\IParserService;
|
||||
use ZBateson\MailMimeParser\Parser\Part\ParserPartStreamContainerFactory;
|
||||
@@ -22,20 +23,12 @@ use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
*/
|
||||
class ParserUUEncodedPartProxyFactory extends ParserPartProxyFactory
|
||||
{
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
protected StreamFactory $streamFactory;
|
||||
|
||||
protected ParserPartStreamContainerFactory $parserPartStreamContainerFactory;
|
||||
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
StreamFactory $sdf,
|
||||
ParserPartStreamContainerFactory $parserPartStreamContainerFactory
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly StreamFactory $streamFactory,
|
||||
protected readonly ParserPartStreamContainerFactory $parserPartStreamContainerFactory,
|
||||
protected readonly string $defaultFallbackCharset = 'ISO-8859-1'
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->streamFactory = $sdf;
|
||||
$this->parserPartStreamContainerFactory = $parserPartStreamContainerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,12 +39,15 @@ class ParserUUEncodedPartProxyFactory extends ParserPartProxyFactory
|
||||
$parserProxy = new ParserUUEncodedPartProxy($partBuilder, $parser);
|
||||
$streamContainer = $this->parserPartStreamContainerFactory->newInstance($parserProxy);
|
||||
|
||||
$parent = $partBuilder->getParent()?->getPart();
|
||||
\assert($parent === null || $parent instanceof IMimePart);
|
||||
$part = new UUEncodedPart(
|
||||
$parserProxy->getUnixFileMode(),
|
||||
$parserProxy->getFileName(),
|
||||
$partBuilder->getParent()->getPart(),
|
||||
$parent,
|
||||
$this->logger,
|
||||
$streamContainer
|
||||
$streamContainer,
|
||||
$this->defaultFallbackCharset
|
||||
);
|
||||
$parserProxy->setPart($part);
|
||||
|
||||
|
||||
@@ -39,9 +39,8 @@ class HeaderStream extends MessagePartStreamDecorator implements SplObserver, St
|
||||
parent::__construct($part);
|
||||
$part->attach($this);
|
||||
|
||||
// unsetting the property forces the first access to go through
|
||||
// __get().
|
||||
unset($this->stream);
|
||||
// Don't initialize $stream - let the StreamDecoratorTrait's __get()
|
||||
// call createStream() lazily when needed.
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@@ -61,12 +60,14 @@ class HeaderStream extends MessagePartStreamDecorator implements SplObserver, St
|
||||
*
|
||||
* If the part is not a MimePart, Content-Type, Content-Disposition and
|
||||
* Content-Transfer-Encoding headers are generated manually.
|
||||
*
|
||||
* @return Traversable<int, array{0: string, 1: string}>
|
||||
*/
|
||||
private function getPartHeadersIterator() : Traversable
|
||||
{
|
||||
if ($this->part instanceof IMimePart) {
|
||||
return $this->part->getRawHeaderIterator();
|
||||
} elseif ($this->part->getParent() !== null && $this->part->getParent()->isMime()) {
|
||||
} elseif ($this->part->getParent()?->isMime()) {
|
||||
return new ArrayIterator([
|
||||
[HeaderConsts::CONTENT_TYPE, $this->part->getContentType()],
|
||||
[HeaderConsts::CONTENT_DISPOSITION, $this->part->getContentDisposition()],
|
||||
|
||||
@@ -56,9 +56,8 @@ class MessagePartStream extends MessagePartStreamDecorator implements SplObserve
|
||||
$this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
$part->attach($this);
|
||||
|
||||
// unsetting the property forces the first access to go through
|
||||
// __get().
|
||||
unset($this->stream);
|
||||
// Don't initialize $stream - let the StreamDecoratorTrait's __get()
|
||||
// call createStream() lazily when needed.
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@@ -69,8 +68,7 @@ class MessagePartStream extends MessagePartStreamDecorator implements SplObserve
|
||||
public function update(SplSubject $subject) : void
|
||||
{
|
||||
if ($this->appendStream !== null) {
|
||||
// unset forces recreation in StreamDecoratorTrait with a call to __get
|
||||
unset($this->stream);
|
||||
$this->stream = null;
|
||||
$this->appendStream = null;
|
||||
}
|
||||
}
|
||||
@@ -121,9 +119,7 @@ class MessagePartStream extends MessagePartStreamDecorator implements SplObserve
|
||||
$boundary = $part->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'boundary');
|
||||
if ($boundary === null) {
|
||||
return \array_map(
|
||||
function($child) {
|
||||
return $child->getStream();
|
||||
},
|
||||
fn($child) => $child->getStream(),
|
||||
$part->getChildParts()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,20 +20,42 @@ use ZBateson\MailMimeParser\Message\IMessagePart;
|
||||
class MessagePartStreamDecorator implements StreamInterface
|
||||
{
|
||||
use StreamDecoratorTrait {
|
||||
StreamDecoratorTrait::__construct as private traitConstruct;
|
||||
read as private decoratorRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var IMessagePart The part to read from.
|
||||
*/
|
||||
protected ?StreamInterface $stream = null;
|
||||
|
||||
protected IMessagePart $part;
|
||||
|
||||
protected ?StreamInterface $stream;
|
||||
|
||||
public function __construct(IMessagePart $part, ?StreamInterface $stream = null)
|
||||
{
|
||||
public function __construct(
|
||||
IMessagePart $part,
|
||||
?StreamInterface $stream = null
|
||||
) {
|
||||
$this->part = $part;
|
||||
$this->stream = $stream;
|
||||
if ($stream !== null) {
|
||||
$this->stream = $stream;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying stream, lazily creating it via createStream() if
|
||||
* not yet initialized.
|
||||
*/
|
||||
protected function resolveStream() : StreamInterface
|
||||
{
|
||||
if ($this->stream === null) {
|
||||
$this->stream = $this->createStream();
|
||||
}
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
public function __get(string $name) : StreamInterface
|
||||
{
|
||||
if ($name === 'stream') {
|
||||
return $this->resolveStream();
|
||||
}
|
||||
throw new \UnexpectedValueException("$name not found on class");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +67,7 @@ class MessagePartStreamDecorator implements StreamInterface
|
||||
public function read(int $length) : string
|
||||
{
|
||||
try {
|
||||
return $this->decoratorRead($length);
|
||||
return $this->resolveStream()->read($length);
|
||||
} catch (MessagePartStreamReadException $me) {
|
||||
throw $me;
|
||||
} catch (RuntimeException $e) {
|
||||
@@ -58,4 +80,96 @@ class MessagePartStreamDecorator implements StreamInterface
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function close() : void
|
||||
{
|
||||
$this->resolveStream()->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return $this->resolveStream()->getMetadata($key);
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
return $this->resolveStream()->detach();
|
||||
}
|
||||
|
||||
public function getSize() : ?int
|
||||
{
|
||||
return $this->resolveStream()->getSize();
|
||||
}
|
||||
|
||||
public function eof() : bool
|
||||
{
|
||||
return $this->resolveStream()->eof();
|
||||
}
|
||||
|
||||
public function tell() : int
|
||||
{
|
||||
return $this->resolveStream()->tell();
|
||||
}
|
||||
|
||||
public function isReadable() : bool
|
||||
{
|
||||
return $this->resolveStream()->isReadable();
|
||||
}
|
||||
|
||||
public function isWritable() : bool
|
||||
{
|
||||
return $this->resolveStream()->isWritable();
|
||||
}
|
||||
|
||||
public function isSeekable() : bool
|
||||
{
|
||||
return $this->resolveStream()->isSeekable();
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET) : void
|
||||
{
|
||||
$this->resolveStream()->seek($offset, $whence);
|
||||
}
|
||||
|
||||
public function write($string) : int
|
||||
{
|
||||
return $this->resolveStream()->write($string);
|
||||
}
|
||||
|
||||
public function rewind() : void
|
||||
{
|
||||
$this->seek(0);
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
try {
|
||||
if ($this->isSeekable()) {
|
||||
$this->seek(0);
|
||||
}
|
||||
return $this->getContents();
|
||||
} catch (\Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function getContents() : string
|
||||
{
|
||||
return \GuzzleHttp\Psr7\Utils::copyToString($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
/** @var callable $callable */
|
||||
$callable = [$this->resolveStream(), $method];
|
||||
$result = ($callable)(...$args);
|
||||
return $result === $this->stream ? $this : $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,15 +27,9 @@ use ZBateson\StreamDecorators\UUStream;
|
||||
*/
|
||||
class StreamFactory
|
||||
{
|
||||
/**
|
||||
* @var bool if true, saving a content stream with an unsupported charset
|
||||
* will be written in the default charset.
|
||||
*/
|
||||
protected bool $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
|
||||
public function __construct(bool $throwExceptionReadingPartContentFromUnsupportedCharsets)
|
||||
{
|
||||
$this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets;
|
||||
public function __construct(
|
||||
protected readonly bool $throwExceptionReadingPartContentFromUnsupportedCharsets
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,24 +130,15 @@ class StreamFactory
|
||||
|
||||
public function getTransferEncodingDecoratedStream(StreamInterface $stream, ?string $transferEncoding, ?string $filename = null) : StreamInterface
|
||||
{
|
||||
$decorated = null;
|
||||
switch ($transferEncoding) {
|
||||
case 'quoted-printable':
|
||||
$decorated = $this->newQuotedPrintableStream($stream);
|
||||
break;
|
||||
case 'base64':
|
||||
$decorated = $this->newBase64Stream(
|
||||
$this->newChunkSplitStream($stream)
|
||||
);
|
||||
break;
|
||||
case 'x-uuencode':
|
||||
$decorated = $this->newUUStream($stream);
|
||||
if ($filename !== null) {
|
||||
$decorated->setFilename($filename);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return $stream;
|
||||
$decorated = match ($transferEncoding) {
|
||||
'quoted-printable' => $this->newQuotedPrintableStream($stream),
|
||||
'base64' => $this->newBase64Stream($this->newChunkSplitStream($stream)),
|
||||
'x-uuencode' => $this->newUUStream($stream),
|
||||
default => $stream,
|
||||
};
|
||||
if ($transferEncoding === 'x-uuencode' && $filename !== null) {
|
||||
\assert($decorated instanceof UUStream);
|
||||
$decorated->setFilename($filename);
|
||||
}
|
||||
return $decorated;
|
||||
}
|
||||
@@ -169,7 +154,7 @@ class StreamFactory
|
||||
/**
|
||||
* Creates and returns a MessagePartStream
|
||||
*/
|
||||
public function newMessagePartStream(IMessagePart $part) : MessagePartStreamDecorator
|
||||
public function newMessagePartStream(IMessagePart $part) : StreamInterface
|
||||
{
|
||||
return new MessagePartStream($this, $part, $this->throwExceptionReadingPartContentFromUnsupportedCharsets);
|
||||
}
|
||||
@@ -191,7 +176,7 @@ class StreamFactory
|
||||
return new HeaderStream($part);
|
||||
}
|
||||
|
||||
public function newDecoratedMessagePartStream(IMessagePart $part, StreamInterface $stream) : MessagePartStreamDecorator
|
||||
public function newDecoratedMessagePartStream(IMessagePart $part, StreamInterface $stream) : StreamInterface
|
||||
{
|
||||
return new MessagePartStreamDecorator($part, $stream);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,18 @@ use Psr\Log\NullLogger;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\Received\DomainConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\Received\GenericReceivedConsumerService;
|
||||
use ZBateson\MailMimeParser\Header\Consumer\ReceivedConsumerService;
|
||||
use ZBateson\MailMimeParser\Message\Factory\IMessagePartFactory;
|
||||
use ZBateson\MailMimeParser\Message\Factory\IMimePartFactory;
|
||||
use ZBateson\MailMimeParser\Message\Factory\IUUEncodedPartFactory;
|
||||
use ZBateson\MailMimeParser\Message\Factory\PartStreamContainerFactory;
|
||||
use ZBateson\MailMimeParser\Message\PartStreamContainer;
|
||||
use ZBateson\MailMimeParser\Parser\HeaderParserService;
|
||||
use ZBateson\MailMimeParser\Parser\MimeParserService;
|
||||
use ZBateson\MailMimeParser\Parser\Part\ParserPartStreamContainerFactory;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserMessageProxyFactory;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserMimePartProxyFactory;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserNonMimeMessageProxyFactory;
|
||||
use ZBateson\MailMimeParser\Parser\Proxy\ParserUUEncodedPartProxyFactory;
|
||||
use ZBateson\MailMimeParser\Stream\StreamFactory;
|
||||
|
||||
return [
|
||||
@@ -26,6 +33,11 @@ return [
|
||||
// header parts
|
||||
'throwExceptionReadingPartContentFromUnsupportedCharsets' => false,
|
||||
|
||||
// Fallback charset for text/* content parts without a declared charset.
|
||||
// Per RFC 2045, the default is ISO-8859-1 but many modern messages omit the
|
||||
// charset and are actually UTF-8. Override this to 'UTF-8' if desired.
|
||||
'defaultFallbackCharset' => 'ISO-8859-1',
|
||||
|
||||
// Maximum multipart nesting depth before parsing stops with a recorded error.
|
||||
'maxMimePartDepth' => 256,
|
||||
|
||||
@@ -70,6 +82,34 @@ return [
|
||||
->constructor(
|
||||
throwExceptionReadingPartContentFromUnsupportedCharsets: new Reference('throwExceptionReadingPartContentFromUnsupportedCharsets')
|
||||
),
|
||||
IMessagePartFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
IMimePartFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
IUUEncodedPartFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
ParserMimePartProxyFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
ParserUUEncodedPartProxyFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
ParserMessageProxyFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
ParserNonMimeMessageProxyFactory::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
defaultFallbackCharset: new Reference('defaultFallbackCharset')
|
||||
),
|
||||
HeaderParserService::class => (new AutowireDefinitionHelper())
|
||||
->constructor(
|
||||
maxHeaderCount: new Reference('maxHeaderCount'),
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.0.6
|
||||
4.0.3
|
||||
|
||||
Reference in New Issue
Block a user