Completed
Push — master ( 01eb7b...2ee407 )
by Alejandro
09:40 queued 07:02
created

MailServiceAbstractFactory::__invoke()   C

Complexity

Conditions 11
Paths 18

Size

Total Lines 59
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 11.0036

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 59
ccs 31
cts 32
cp 0.9688
rs 6.3545
cc 11
eloc 36
nc 18
nop 3
crap 11.0036

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 13
     * @return object
45
     * @throws ServiceNotFoundException if unable to resolve the service.
46 13
     * @throws ServiceNotCreatedException if an exception is raised when
47 13
     *     creating a service.
48 13
     * @throws ContainerException if any other error occurs
49
     */
50
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
51
    {
52 13
        $specificServiceName = explode('.', $requestedName)[2];
53 13
        $this->mailOptions = $container->get(
54 11
            sprintf('%s.%s.%s', self::ACMAILER_PART, MailOptionsAbstractFactory::SPECIFIC_PART, $specificServiceName)
55 11
        );
56
57
        // Create the service
58 11
        $message        = $this->createMessage();
59
        $transport      = $this->createTransport($container);
60
        $renderer       = $this->createRenderer($container);
61 11
        $mailService    = new MailService($message, $transport, $renderer);
62 11
63 2
        // Set subject
64 2
        $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 1
66 1
        // Set body, either by using a template or a raw body
67
        $body = $this->mailOptions->getMessageOptions()->getBody();
68 1
        if ($body->getUseTemplate()) {
69 1
            $defaultLayoutConfig = $body->getTemplate()->getDefaultLayout();
70
            if (isset($defaultLayoutConfig['path'])) {
71 2
                $params = isset($defaultLayoutConfig['params']) ? $defaultLayoutConfig['params'] : [];
72
                $captureTo = isset($defaultLayoutConfig['template_capture_to'])
73 9
                    ? $defaultLayoutConfig['template_capture_to']
74
                    : 'content';
75
                $mailService->setDefaultLayout(new DefaultLayout($defaultLayoutConfig['path'], $params, $captureTo));
76
            }
77 11
            $mailService->setTemplate($body->getTemplate()->toViewModel(), ['charset' => $body->getCharset()]);
78 11
        } else {
79
            $mailService->setBody($body->getContent(), $body->getCharset());
80
        }
81 11
82 11
        // Attach files
83 1
        $files = $this->mailOptions->getMessageOptions()->getAttachments()->getFiles();
84 1
        $mailService->addAttachments($files);
85 1
86 1
        // Attach files from dir
87
        $dir = $this->mailOptions->getMessageOptions()->getAttachments()->getDir();
88 1
        if ($dir['iterate'] === true && is_string($dir['path']) && is_dir($dir['path'])) {
89
            $files = $dir['recursive'] === true ?
90
                new \RecursiveIteratorIterator(
91 1
                    new \RecursiveDirectoryIterator($dir['path'], \RecursiveDirectoryIterator::SKIP_DOTS),
92 1
                    \RecursiveIteratorIterator::CHILD_FIRST
93 1
                ):
94
                new \DirectoryIterator($dir['path']);
95 1
96
            /* @var \SplFileInfo $fileInfo */
97
            foreach ($files as $fileInfo) {
98
                if ($fileInfo->isDir()) {
99
                    continue;
100 11
                }
101 10
                $mailService->addAttachment($fileInfo->getPathname());
102
            }
103
        }
104
105
        // Attach mail listeners
106
        $this->attachMailListeners($mailService, $container);
107 13
        return $mailService;
108
    }
109 13
110
    /**
111 13
     * @return Message
112 13
     */
113 13
    protected function createMessage()
114 1
    {
115
        $options = $this->mailOptions->getMessageOptions();
116 13
        // Prepare Mail Message
117 13
        $message = new Message();
118 1
        $from = $options->getFrom();
119
        if (! empty($from)) {
120 13
            $message->setFrom($from, $options->getFromName());
121 13
        }
122 1
        $replyTo = $options->getReplyTo();
123
        if (! empty($replyTo)) {
124 13
            $message->setReplyTo($replyTo, $options->getReplyToName());
125 13
        }
126 1
        $to = $options->getTo();
127
        if (! empty($to)) {
128 13
            $message->setTo($to);
129 13
        }
130 1
        $cc = $options->getCc();
131
        if (! empty($cc)) {
132
            $message->setCc($cc);
133 13
        }
134
        $bcc = $options->getBcc();
135
        if (! empty($bcc)) {
136
            $message->setBcc($bcc);
137
        }
138
139
        return $message;
140 13
    }
141
142 13
    /**
143
     * @param ContainerInterface $container
144 13
     * @return TransportInterface
145 1
     */
146
    protected function createTransport(ContainerInterface $container)
147
    {
148
        $adapter = $this->mailOptions->getMailAdapter();
149 12
        // A transport instance can be returned as is
150
        if ($adapter instanceof TransportInterface) {
151 2
            return $this->setupTransportConfig($adapter);
152 2
        }
153 1
154
        // Check if the adapter is a service
155 1
        if (is_string($adapter) && $container->has($adapter)) {
156 1
            /** @var TransportInterface $transport */
157
            $transport = $container->get($adapter);
158
            if ($transport instanceof TransportInterface) {
159
                return $this->setupTransportConfig($transport);
160
            } else {
161
                throw new InvalidArgumentException(
162 10
                    'Provided mail_adapter service does not return a "Zend\Mail\Transport\TransportInterface" instance'
163 9
                );
164
            }
165
        }
166
167 1
        // Check if the adapter is one of Zend's default adapters
168 1
        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 1
            return $this->setupTransportConfig(new $adapter());
170
        }
171
172
        // The adapter is not valid. Throw an exception
173
        throw new InvalidArgumentException(sprintf(
174
            'mail_adapter must be an instance of "Zend\Mail\Transport\TransportInterface" or string, "%s" provided',
175
            is_object($adapter) ? get_class($adapter) : gettype($adapter)
176
        ));
177 11
    }
178
179 11
    /**
180 1
     * @param TransportInterface $transport
181
     * @return TransportInterface
182 1
     */
183
    protected function setupTransportConfig(TransportInterface $transport)
184
    {
185 11
        if ($transport instanceof Smtp) {
186
            $transport->setOptions($this->mailOptions->getSmtpOptions());
187
        } elseif ($transport instanceof File) {
188
            $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
        }
190
191
        return $transport;
192 11
    }
193
194
    /**
195 11
     * @param ContainerInterface $container
196
     * @return RendererInterface
197
     */
198 11
    protected function createRenderer(ContainerInterface $container)
199 3
    {
200 9
        // Try to return the configured renderer. If it points to an undefined service, create a renderer on the fly
201
        $serviceName = $this->mailOptions->getRenderer();
202 9
203 9
        try {
204
            $renderer = $container->get($serviceName);
205
            return $renderer;
206 9
        } catch (ServiceNotFoundException $e) {
207
            // In case the renderer service is not defined, try to construct it
208 1
            $vmConfig = $this->getSpecificConfig($container, 'view_manager');
209 1
            $renderer = new PhpRenderer();
210 1
211 1
            // Check what kind of view_manager configuration has been defined
212 1
            if (isset($vmConfig['template_map']) && isset($vmConfig['template_path_stack'])) {
213 1
                // If both a template_map and a template_path_stack have been defined, create an AggregateResolver
214 9
                $pathStackResolver = new TemplatePathStack();
215
                $pathStackResolver->setPaths($vmConfig['template_path_stack']);
216 1
                $resolver = new AggregateResolver();
217 9
                $resolver->attach($pathStackResolver)
218
                    ->attach(new TemplateMapResolver($vmConfig['template_map']));
219 9
                $renderer->setResolver($resolver);
220 9
            } elseif (isset($vmConfig['template_map'])) {
221 9
                // Create a TemplateMapResolver in case only the template_map has been defined
222
                $renderer->setResolver(new TemplateMapResolver($vmConfig['template_map']));
223
            } 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
                $renderer->setResolver($pathStackResolver);
228
            }
229
230
            // Create a HelperPluginManager with default view helpers and user defined view helpers
231
            $renderer->setHelperPluginManager($this->createHelperPluginManager($container));
232
            return $renderer;
233
        }
234
    }
235 9
236
    /**
237 9
     * Creates a view helper manager
238
     * @param ContainerInterface $container
239 9
     * @return HelperPluginManager
240 9
     */
241 9
    protected function createHelperPluginManager(ContainerInterface $container)
242 9
    {
243
        $factory = new ViewHelperManagerFactory();
244
        /** @var HelperPluginManager $helperManager */
245
        $helperManager = $factory->__invoke($container, ViewHelperManagerFactory::PLUGIN_MANAGER_CLASS);
246
        $config = new Config($this->getSpecificConfig($container, 'view_helpers'));
247
        $config->configureServiceManager($helperManager);
248
        return $helperManager;
249
    }
250
251 9
    /**
252
     * Returns a specific configuration defined by provided key
253 9
     * @param ContainerInterface $container
254 9
     * @param $configKey
255
     * @return array
256
     */
257
    protected function getSpecificConfig(ContainerInterface $container, $configKey)
258
    {
259
        $config = $container->get('Config');
260
        return ! empty($config) && isset($config[$configKey]) ? $config[$configKey] : [];
261
    }
262
263
    /**
264 11
     * Attaches the preconfigured mail listeners to the mail service
265
     *
266 11
     * @param MailListenerAwareInterface $service
267 11
     * @param ContainerInterface $container
268
     * @throws InvalidArgumentException
269 2
     */
270 1
    protected function attachMailListeners(MailListenerAwareInterface $service, ContainerInterface $container)
271 2
    {
272 1
        $listeners = $this->mailOptions->getMailListeners();
273
        foreach ($listeners as $listener) {
274
            // Try to fetch the listener from the ServiceManager or lazily create an instance
275
            if (is_string($listener) && $container->has($listener)) {
276 2
                $listener = $container->get($listener);
277 1
            } elseif (is_string($listener) && class_exists($listener)) {
278
                $listener = new $listener();
279 1
            }
280 1
281
            // At this point, the listener should be an instance of MailListenerInterface, otherwise it is invalid
282
            if (! $listener instanceof MailListenerInterface) {
283 1
                throw new InvalidArgumentException(sprintf(
284
                    'Provided listener of type "%s" is not valid. '
285 10
                    . 'Expected "string" or "AcMailer\Listener\MailListenerInterface"',
286
                    is_object($listener) ? get_class($listener) : gettype($listener)
287
                ));
288
            }
289
            $service->attachMailListener($listener);
290
        }
291
    }
292
}
293