Passed
Push — master ( 46ed75...ca2387 )
by Zaahid
03:33
created

MailMimeParser   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Test Coverage

Coverage 58.81%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 11
eloc 33
c 4
b 0
f 0
dl 0
loc 124
ccs 20
cts 34
cp 0.5881
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 5
A getGlobalContainer() 0 9 2
A parse() 0 10 2
A setGlobalLogger() 0 3 1
A setGlobalPhpDiConfiguration() 0 7 1
1
<?php
2
/**
3
 * This file is part of the ZBateson\MailMimeParser project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
8
namespace ZBateson\MailMimeParser;
9
10
use GuzzleHttp\Psr7\CachingStream;
11
use GuzzleHttp\Psr7\Utils;
12
use Psr\Http\Message\StreamInterface;
13
use Psr\Log\LoggerInterface;
14
use DI\Container;
15
use DI\ContainerBuilder;
16
use DI\Definition\Source\DefinitionSource;
17
use ZBateson\MailMimeParser\Parser\MessageParserService;
18
19
/**
20
 * Parses a MIME message into an {@see IMessage} object.
21
 *
22
 * The class sets up the dependency injection container (using PHP-DI) with the
23
 * ability to override and/or provide specialized classes.  To override you can:
24
 *
25
 *  - Provide an array|string|DefinitionSource to the constructor to affect
26
 *    classes used on a single instance of MailMimeParser
27
 *  - Call MailMimeParser::setGlobalPhpDiConfiguration with an
28
 *    array|string|DefinitionSource to to override it globally on all instances
29
 *    of MailMimeParser
30
 *  - Call MailMimeParser::getGlobalContainer(), and use set() to override
31
 *    individual definitions globally.
32
 *
33
 * You may also provide a LoggerInterface on the constructor for a single
34
 * instance, or override it globally by calling setGlobalLogger.  This is the
35
 * same as setting up Psr\Log\LoggerInterface with your logger class in a Php-Di
36
 * configuration in one of the above methods.
37
 *
38
 * To invoke the parser, call `parse` on a MailMimeParser object.
39
 *
40
 * ```php
41
 * $parser = new MailMimeParser();
42
 * // the resource is attached due to the second parameter being true and will
43
 * // be closed when the returned IMessage is destroyed
44
 * $message = $parser->parse(fopen('path/to/file.txt'), true);
45
 * // use $message here
46
 * ```
47
 *
48
 * @author Zaahid Bateson
49
 */
50
class MailMimeParser
51
{
52
    /**
53
     * @var string the default charset used to encode strings (or string content
54
     *      like streams) returned by MailMimeParser (for e.g. the string
55
     *      returned by calling $message->getTextContent()).
56
     */
57
    public const DEFAULT_CHARSET = 'UTF-8';
58
59
    /**
60
     * @var Container The instance's dependency injection container.
61
     */
62
    protected Container $container;
63
64
    /**
65
     * @var Container The static global container
66
     */
67
    private static ?Container $globalContainer = null;
68
69
    /**
70
     * @var MessageParserService for parsing messages
71
     */
72
    protected MessageParserService $messageParser;
73
74
    /**
75
     * Returns the global php-di container instance.
76
     *
77
     * @return Container
78
     */
79 108
    public static function getGlobalContainer() : Container
80
    {
81 108
        if (self::$globalContainer === null) {
82
            $builder = new ContainerBuilder();
83
            $builder->useAttributes(true);
84
            $builder->addDefinitions(__DIR__ . '/di_config.php');
85
            self::$globalContainer = $builder->build();
86
        }
87 108
        return self::$globalContainer;
88
    }
89
90
    /**
91
     * Sets global configuration for php-di.
92
     */
93
    public static function setGlobalPhpDiConfiguration(array|string|DefinitionSource $phpDiConfig) : void
94
    {
95
        $container = self::getGlobalContainer();
96
        $builder = new ContainerBuilder();
97
        $builder->wrapContainer($container);
98
        $builder->addDefinitions($phpDiConfig);
99
        self::$globalContainer = $builder->build();
100
    }
101
102
    /**
103
     * Registers the provided logger globally.
104
     *
105
     * @param LoggerInterface $logger
106
     * @return void
107
     */
108
    public static function setGlobalLogger(LoggerInterface $logger) : void
109
    {
110
        self::getGlobalContainer()->set(LoggerInterface::class, $logger);
111
    }
112
113
    /**
114
     * Provide custom php-di configuration to customize dependency injection, or
115
     * provide a custom logger for the instance only.
116
     *
117
     * Note: this only affects instances created through this instance of the
118
     * MailMimeParser, or the container itself.  Calling 'new MimePart()'
119
     * directly for instance, would use the global service locator to setup any
120
     * dependencies MimePart needs.  This applies to a provided $logger too --
121
     * it would only affect instances of objects created through the provided
122
     * MailMimeParser.
123
     *
124
     * @see MailMimeParser::setGlobalPhpDiConfiguration() to register
125
     *      configuration globally.
126
     * @see MailMimeParser::setGlobalLogger() to set a global logger
127
     * @param ?LoggerInterface $logger
128
     * @param ?array[] $phpDiContainerConfig
129
     */
130 108
    public function __construct(?LoggerInterface $logger = null, array|string|DefinitionSource|null $phpDiContainerConfig = null)
131
    {
132 108
        $this->container = self::getGlobalContainer();
133 108
        if ($phpDiContainerConfig !== null || $logger !== null) {
134 3
            $builder = new ContainerBuilder();
135 3
            $builder->wrapContainer($this->container);
136 3
            if ($phpDiContainerConfig !== null) {
137 3
                $builder->addDefinitions($phpDiContainerConfig);
138
            }
139 3
            if ($logger !== null) {
140
                $builder->addDefinitions([ LoggerInterface::class => $logger ]);
141
            }
142 3
            $this->container = $builder->build();
143
        }
144 108
        $this->messageParser = $this->container->get(MessageParserService::class);
145
    }
146
147
    /**
148
     * Parses the passed stream handle or string into an {@see IMessage} object
149
     * and returns it.
150
     *
151
     * If the passed $resource is a resource handle or StreamInterface, the
152
     * resource must remain open while the returned IMessage object exists.
153
     * Pass true as the second argument to have the resource attached to the
154
     * IMessage and closed for you when it's destroyed, or pass false to
155
     * manually close it if it should remain open after the IMessage object is
156
     * destroyed.
157
     *
158
     * @param resource|StreamInterface|string $resource The resource handle to
159
     *        the input stream of the mime message, or a string containing a
160
     *        mime message.
161
     * @param bool $attached pass true to have it attached to the returned
162
     *        IMessage and destroyed with it.
163
     */
164 108
    public function parse(mixed $resource, bool $attached) : IMessage
165
    {
166 108
        $stream = Utils::streamFor(
167 108
            $resource,
168 108
            ['metadata' => ['mmp-detached-stream' => ($attached !== true)]]
169 108
        );
170 108
        if (!$stream->isSeekable()) {
171
            $stream = new CachingStream($stream);
172
        }
173 108
        return $this->messageParser->parse($stream);
174
    }
175
}
176