Completed
Push — master ( 12bac9...cd01bf )
by Alejandro
9s
created

MailServiceAbstractFactory::setupTransportConfig()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 6
nc 3
nop 1
crap 3
1
<?php
2
namespace AcMailer\Service\Factory;
3
4
use AcMailer\Event\MailListenerAwareInterface;
5
use AcMailer\Event\MailListenerInterface;
6
use AcMailer\Exception\InvalidArgumentException;
7
use AcMailer\Factory\AbstractAcMailerFactory;
8
use AcMailer\Options\Factory\MailOptionsAbstractFactory;
9
use AcMailer\Options\MailOptions;
10
use AcMailer\Service\MailService;
11
use AcMailer\View\DefaultLayout;
12
use Interop\Container\ContainerInterface;
13
use Interop\Container\Exception\ContainerException;
14
use Zend\Mail\Message;
15
use Zend\Mail\Transport\File;
16
use Zend\Mail\Transport\Smtp;
17
use Zend\Mail\Transport\TransportInterface;
18
use Zend\Mvc\Service\ViewHelperManagerFactory;
19
use Zend\ServiceManager\Config;
20
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
21
use Zend\ServiceManager\Exception\ServiceNotFoundException;
22
use Zend\View\HelperPluginManager;
23
use Zend\View\Renderer\PhpRenderer;
24
use Zend\View\Renderer\RendererInterface;
25
use Zend\View\Resolver\AggregateResolver;
26
use Zend\View\Resolver\TemplateMapResolver;
27
use Zend\View\Resolver\TemplatePathStack;
28
29
class MailServiceAbstractFactory extends AbstractAcMailerFactory
30
{
31
    const SPECIFIC_PART = 'mailservice';
32
33
    /**
34
     * @var MailOptions
35
     */
36
    protected $mailOptions;
37
38
    /**
39
     * Create an object
40
     *
41
     * @param  ContainerInterface $container
42
     * @param  string $requestedName
43
     * @param  null|array $options
44
     * @return object
45
     * @throws ServiceNotFoundException if unable to resolve the service.
46
     * @throws ServiceNotCreatedException if an exception is raised when
47
     *     creating a service.
48
     * @throws ContainerException if any other error occurs
49
     */
50 13
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
51
    {
52 13
        $specificServiceName = explode('.', $requestedName)[2];
53 13
        $this->mailOptions = $container->get(
54 13
            sprintf('%s.%s.%s', self::ACMAILER_PART, MailOptionsAbstractFactory::SPECIFIC_PART, $specificServiceName)
55 13
        );
56
57
        // Create the service
58 13
        $message        = $this->createMessage();
59 13
        $transport      = $this->createTransport($container);
60 11
        $renderer       = $this->createRenderer($container);
61 11
        $mailService    = new MailService($message, $transport, $renderer);
62
63
        // Set subject
64 11
        $mailService->setSubject($this->mailOptions->getMessageOptions()->getSubject());
0 ignored issues
show
Deprecated Code introduced by
The method AcMailer\Service\MailService::setSubject() has been deprecated with message: Use $mailService->getMessage()->setSubject() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
65
66
        // Set body, either by using a template or a raw body
67 11
        $body = $this->mailOptions->getMessageOptions()->getBody();
68 11
        if ($body->getUseTemplate()) {
69 2
            $defaultLayoutConfig = $body->getTemplate()->getDefaultLayout();
70 2
            if (isset($defaultLayoutConfig['path'])) {
71 1
                $params = isset($defaultLayoutConfig['params']) ? $defaultLayoutConfig['params'] : [];
72 1
                $captureTo = isset($defaultLayoutConfig['template_capture_to'])
73 1
                    ? $defaultLayoutConfig['template_capture_to']
74 1
                    : 'content';
75 2
                $mailService->setDefaultLayout(new DefaultLayout($defaultLayoutConfig['path'], $params, $captureTo));
76 1
            }
77 2
            $mailService->setTemplate($body->getTemplate()->toViewModel(), ['charset' => $body->getCharset()]);
78 2
        } else {
79 9
            $mailService->setBody($body->getContent(), $body->getCharset());
80
        }
81
82
        // Attach files
83 11
        $files = $this->mailOptions->getMessageOptions()->getAttachments()->getFiles();
84 11
        $mailService->addAttachments($files);
85
86
        // Attach files from dir
87 11
        $dir = $this->mailOptions->getMessageOptions()->getAttachments()->getDir();
88 11
        if ($dir['iterate'] === true && is_string($dir['path']) && is_dir($dir['path'])) {
89 1
            $files = $dir['recursive'] === true ?
90 1
                new \RecursiveIteratorIterator(
91 1
                    new \RecursiveDirectoryIterator($dir['path'], \RecursiveDirectoryIterator::SKIP_DOTS),
92
                    \RecursiveIteratorIterator::CHILD_FIRST
93 1
                ):
94 1
                new \DirectoryIterator($dir['path']);
95
96
            /* @var \SplFileInfo $fileInfo */
97 1
            foreach ($files as $fileInfo) {
98 1
                if ($fileInfo->isDir()) {
99 2
                    continue;
100
                }
101 1
                $mailService->addAttachment($fileInfo->getPathname());
102 1
            }
103 1
        }
104
105
        // Attach mail listeners
106 11
        $this->attachMailListeners($mailService, $container);
107 10
        return $mailService;
108
    }
109
110
    /**
111
     * @return Message
112
     */
113 13
    protected function createMessage()
114
    {
115 13
        $options = $this->mailOptions->getMessageOptions();
116
        // Prepare Mail Message
117 13
        $message = new Message();
118 13
        $from = $options->getFrom();
119 13
        if (! empty($from)) {
120 1
            $message->setFrom($from, $options->getFromName());
121 1
        }
122 13
        $replyTo = $options->getReplyTo();
123 13
        if (! empty($replyTo)) {
124 1
            $message->setReplyTo($replyTo, $options->getReplyToName());
125 1
        }
126 13
        $to = $options->getTo();
127 13
        if (! empty($to)) {
128 1
            $message->setTo($to);
129 1
        }
130 13
        $cc = $options->getCc();
131 13
        if (! empty($cc)) {
132 1
            $message->setCc($cc);
133 1
        }
134 13
        $bcc = $options->getBcc();
135 13
        if (! empty($bcc)) {
136 1
            $message->setBcc($bcc);
137 1
        }
138
139 13
        return $message;
140
    }
141
142
    /**
143
     * @param ContainerInterface $container
144
     * @return TransportInterface
145
     */
146 13
    protected function createTransport(ContainerInterface $container)
147
    {
148 13
        $adapter = $this->mailOptions->getMailAdapter();
149
        // A transport instance can be returned as is
150 13
        if ($adapter instanceof TransportInterface) {
151 1
            return $this->setupTransportConfig($adapter);
152
        }
153
154
        // Check if the adapter is a service
155 12
        if (is_string($adapter) && $container->has($adapter)) {
156
            /** @var TransportInterface $transport */
157 2
            $transport = $container->get($adapter);
158 2
            if ($transport instanceof TransportInterface) {
159 1
                return $this->setupTransportConfig($transport);
160
            } else {
161 1
                throw new InvalidArgumentException(
162
                    'Provided mail_adapter service does not return a "Zend\Mail\Transport\TransportInterface" instance'
163 1
                );
164
            }
165
        }
166
167
        // Check if the adapter is one of Zend's default adapters
168 10
        if (is_string($adapter) && is_subclass_of($adapter, 'Zend\Mail\Transport\TransportInterface')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
169 9
            return $this->setupTransportConfig(new $adapter());
170
        }
171
172
        // The adapter is not valid. Throw an exception
173 1
        throw new InvalidArgumentException(sprintf(
174 1
            'mail_adapter must be an instance of "Zend\Mail\Transport\TransportInterface" or string, "%s" provided',
175 1
            is_object($adapter) ? get_class($adapter) : gettype($adapter)
176 1
        ));
177
    }
178
179
    /**
180
     * @param TransportInterface $transport
181
     * @return TransportInterface
182
     */
183 11
    protected function setupTransportConfig(TransportInterface $transport)
184
    {
185 11
        if ($transport instanceof Smtp) {
186 1
            $transport->setOptions($this->mailOptions->getSmtpOptions());
187 11
        } elseif ($transport instanceof File) {
188 1
            $transport->setOptions($this->mailOptions->getFileOptions());
0 ignored issues
show
Bug introduced by
It seems like $this->mailOptions->getFileOptions() can be null; however, setOptions() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
189 1
        }
190
191 11
        return $transport;
192
    }
193
194
    /**
195
     * @param ContainerInterface $container
196
     * @return RendererInterface
197
     */
198 11
    protected function createRenderer(ContainerInterface $container)
199
    {
200
        // Try to return the configured renderer. If it points to an undefined service, create a renderer on the fly
201 11
        $serviceName = $this->mailOptions->getRenderer();
202
203
        try {
204 11
            $renderer = $container->get($serviceName);
205 3
            return $renderer;
206 9
        } catch (ServiceNotFoundException $e) {
207
            // In case the renderer service is not defined, try to construct it
208 9
            $vmConfig = $this->getSpecificConfig($container, 'view_manager');
209 9
            $renderer = new PhpRenderer();
210
211
            // Check what kind of view_manager configuration has been defined
212 9
            if (isset($vmConfig['template_map']) && isset($vmConfig['template_path_stack'])) {
213
                // If both a template_map and a template_path_stack have been defined, create an AggregateResolver
214 1
                $pathStackResolver = new TemplatePathStack();
215 1
                $pathStackResolver->setPaths($vmConfig['template_path_stack']);
216 1
                $resolver = new AggregateResolver();
217 1
                $resolver->attach($pathStackResolver)
218 1
                    ->attach(new TemplateMapResolver($vmConfig['template_map']));
219 1
                $renderer->setResolver($resolver);
220 9
            } elseif (isset($vmConfig['template_map'])) {
221
                // Create a TemplateMapResolver in case only the template_map has been defined
222 1
                $renderer->setResolver(new TemplateMapResolver($vmConfig['template_map']));
223 9
            } elseif (isset($vmConfig['template_path_stack'])) {
224
                // Create a TemplatePathStack resolver in case only the template_path_stack has been defined
225 9
                $pathStackResolver = new TemplatePathStack();
226 9
                $pathStackResolver->setPaths($vmConfig['template_path_stack']);
227 9
                $renderer->setResolver($pathStackResolver);
228 9
            }
229
230
            // Create a HelperPluginManager with default view helpers and user defined view helpers
231 9
            $renderer->setHelperPluginManager($this->createHelperPluginManager($container));
232 9
            return $renderer;
233
        }
234
    }
235
236
    /**
237
     * Creates a view helper manager
238
     * @param ContainerInterface $container
239
     * @return HelperPluginManager
240
     */
241 9
    protected function createHelperPluginManager(ContainerInterface $container)
242
    {
243 9
        $factory = new ViewHelperManagerFactory();
244
        /** @var HelperPluginManager $helperManager */
245 9
        $helperManager = $factory->__invoke($container, ViewHelperManagerFactory::PLUGIN_MANAGER_CLASS);
246 9
        $config = new Config($this->getSpecificConfig($container, 'view_helpers'));
247 9
        $config->configureServiceManager($helperManager);
248 9
        return $helperManager;
249
    }
250
251
    /**
252
     * Returns a specific configuration defined by provided key
253
     * @param ContainerInterface $container
254
     * @param $configKey
255
     * @return array
256
     */
257 9
    protected function getSpecificConfig(ContainerInterface $container, $configKey)
258
    {
259 9
        $config = $container->get('Config');
260 9
        return ! empty($config) && isset($config[$configKey]) ? $config[$configKey] : [];
261
    }
262
263
    /**
264
     * Attaches the preconfigured mail listeners to the mail service
265
     *
266
     * @param MailListenerAwareInterface $service
267
     * @param ContainerInterface $container
268
     * @throws InvalidArgumentException
269
     */
270 11
    protected function attachMailListeners(MailListenerAwareInterface $service, ContainerInterface $container)
271
    {
272 11
        $listeners = $this->mailOptions->getMailListeners();
273 11
        foreach ($listeners as $listener) {
274
            // Try to fetch the listener from the ServiceManager or lazily create an instance
275 2
            if (is_string($listener) && $container->has($listener)) {
276 1
                $listener = $container->get($listener);
277 2
            } elseif (is_string($listener) && class_exists($listener)) {
278 1
                $listener = new $listener();
279 1
            }
280
281
            // At this point, the listener should be an instance of MailListenerInterface, otherwise it is invalid
282 2
            if (! $listener instanceof MailListenerInterface) {
283 1
                throw new InvalidArgumentException(sprintf(
284
                    'Provided listener of type "%s" is not valid. '
285 1
                    . 'Expected "string" or "AcMailer\Listener\MailListenerInterface"',
286 1
                    is_object($listener) ? get_class($listener) : gettype($listener)
287 1
                ));
288
            }
289 1
            $service->attachMailListener($listener);
290 10
        }
291 10
    }
292
}
293