MailMimeParser::__construct()   A
last analyzed

Complexity

Conditions 6
Paths 9

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.4425

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 14
c 2
b 1
f 0
dl 0
loc 23
ccs 10
cts 13
cp 0.7692
rs 9.2222
cc 6
nc 9
nop 3
crap 6.4425
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 DI\Container;
11
use DI\ContainerBuilder;
12
use DI\Definition\Source\DefinitionSource;
13
use GuzzleHttp\Psr7\CachingStream;
14
use GuzzleHttp\Psr7\Utils;
15
use Psr\Http\Message\StreamInterface;
16
use Psr\Log\LoggerInterface;
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::addGlobalContainerDefinition 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 string the default definition file.
61
     */
62
    private const DEFAULT_DEFINITIONS_FILE = __DIR__ . '/di_config.php';
63
64
    /**
65
     * @var Container The instance's dependency injection container.
66
     */
67
    protected Container $container;
68
69
    /**
70
     * @var MessageParserService for parsing messages
71
     */
72
    protected MessageParserService $messageParser;
73
74
    /**
75
     * @var Container The static global container
76
     */
77
    private static ?Container $globalContainer = null;
78
79
    /**
80
     * @var array<array|string|DefinitionSource> an array of global definitions
81
     *      being used.
82
     */
83
    private static array $globalDefinitions = [self::DEFAULT_DEFINITIONS_FILE];
84
85
    /**
86
     * Returns the default ContainerBuilder with default loaded definitions.
87
     */
88 4
    private static function getGlobalContainerBuilder() : ContainerBuilder
89
    {
90 4
        $builder = new ContainerBuilder();
91 4
        foreach (self::$globalDefinitions as $def) {
92 4
            $builder->addDefinitions($def);
93
        }
94 4
        return $builder;
95
    }
96
97
    /**
98
     * Sets global configuration for php-di.  Overrides all previously set
99
     * definitions.  You can optionally not use the default MMP definitions file
100
     * by passing 'false' to the $useDefaultDefinitionsFile argument.
101
     *
102
     * @var array<array|string|DefinitionSource> array of definitions
103
     */
104
    public static function setGlobalPhpDiConfigurations(array $phpDiConfigs, bool $useDefaultDefinitionsFile = true) : void
105
    {
106
        self::$globalDefinitions = \array_merge(
107
            ($useDefaultDefinitionsFile) ? [self::DEFAULT_DEFINITIONS_FILE] : [],
108
            $phpDiConfigs
109
        );
110
        self::$globalContainer = null;
111
    }
112
113
    public static function addGlobalPhpDiContainerDefinition(array|string|DefinitionSource $phpDiConfig) : void
114
    {
115
        self::$globalDefinitions[] = $phpDiConfig;
116
        self::$globalContainer = null;
117
    }
118
119
    public static function resetGlobalPhpDiContainerDefinitions() : void
120
    {
121
        self::$globalDefinitions = [self::DEFAULT_DEFINITIONS_FILE];
122
        self::$globalContainer = null;
123
    }
124
125
    /**
126
     * Returns the global php-di container instance.
127
     *
128
     */
129 107
    public static function getGlobalContainer() : Container
130
    {
131 107
        if (self::$globalContainer === null) {
132
            $builder = self::getGlobalContainerBuilder();
133
            self::$globalContainer = $builder->build();
134
        }
135 107
        return self::$globalContainer;
136
    }
137
138
    /**
139
     * Registers the provided logger globally.
140
     */
141
    public static function setGlobalLogger(LoggerInterface $logger) : void
142
    {
143
        self::$globalDefinitions[] = [LoggerInterface::class => $logger];
144
        self::$globalContainer = null;
145
    }
146
147
    /**
148
     * Provide custom php-di configuration to customize dependency injection, or
149
     * provide a custom logger for the instance only.
150
     *
151
     * Note: this only affects instances created through this instance of the
152
     * MailMimeParser, or the container itself.  Calling 'new MimePart()'
153
     * directly for instance, would use the global service locator to setup any
154
     * dependencies MimePart needs.  This applies to a provided $logger too --
155
     * it would only affect instances of objects created through the provided
156
     * MailMimeParser.
157
     *
158
     * Passing false to $useGlobalDefinitions will cause MMP to not use any
159
     * global definitions.  The default definitions file
160
     * MailMimeParser::DEFAULT_DEFINITIONS_FILE will still be added though.
161
     *
162
     * @see MailMimeParser::setGlobalPhpDiConfiguration() to register
163
     *      configuration globally.
164
     * @see MailMimeParser::setGlobalLogger() to set a global logger
165
     */
166 110
    public function __construct(
167
        ?LoggerInterface $logger = null,
168
        array|string|DefinitionSource|null $phpDiContainerConfig = null,
169
        bool $useGlobalDefinitions = true
170
    ) {
171 110
        if ($phpDiContainerConfig !== null || $logger !== null) {
172 4
            if ($useGlobalDefinitions) {
173 4
                $builder = self::getGlobalContainerBuilder();
174
            } else {
175
                $builder = new ContainerBuilder();
176
                $builder->addDefinitions(self::DEFAULT_DEFINITIONS_FILE);
177
            }
178 4
            if ($phpDiContainerConfig !== null) {
179 4
                $builder->addDefinitions($phpDiContainerConfig);
180
            }
181 4
            if ($logger !== null) {
182
                $builder->addDefinitions([LoggerInterface::class => $logger]);
183
            }
184 4
            $this->container = $builder->build();
185
        } else {
186 107
            $this->container = self::getGlobalContainer();
187
        }
188 110
        $this->messageParser = $this->container->get(MessageParserService::class);
189
    }
190
191
    /**
192
     * Parses the passed stream handle or string into an {@see IMessage} object
193
     * and returns it.
194
     *
195
     * If the passed $resource is a resource handle or StreamInterface, the
196
     * resource must remain open while the returned IMessage object exists.
197
     * Pass true as the second argument to have the resource attached to the
198
     * IMessage and closed for you when it's destroyed, or pass false to
199
     * manually close it if it should remain open after the IMessage object is
200
     * destroyed.
201
     *
202
     * @param resource|StreamInterface|string $resource The resource handle to
203
     *        the input stream of the mime message, or a string containing a
204
     *        mime message.
205
     * @param bool $attached pass true to have it attached to the returned
206
     *        IMessage and destroyed with it.
207
     */
208 110
    public function parse(mixed $resource, bool $attached) : IMessage
209
    {
210 110
        $stream = Utils::streamFor(
211 110
            $resource,
212 110
            ['metadata' => ['mmp-detached-stream' => ($attached !== true)]]
213 110
        );
214 110
        if (!$stream->isSeekable()) {
215
            $stream = new CachingStream($stream);
216
        }
217 110
        return $this->messageParser->parse($stream);
218
    }
219
}
220