Passed
Pull Request — master (#16)
by Wilmer
04:34 queued 02:07
created

Template   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Test Coverage

Coverage 97.44%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 36
dl 0
loc 136
ccs 38
cts 39
cp 0.9744
rs 10
c 1
b 0
f 0
wmc 14

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B compose() 0 32 8
A render() 0 7 2
A setTextLayout() 0 3 1
A setHtmlLayout() 0 3 1
A getViewPath() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Mailer;
6
7
use Throwable;
8
use Yiisoft\View\Exception\ViewNotFoundException;
9
use Yiisoft\View\ViewContextInterface;
10
use Yiisoft\View\View;
11
12
use function html_entity_decode;
13
use function is_array;
14
use function preg_match;
15
use function preg_replace;
16
use function trim;
17
18
/**
19
 * Template composes the message from view templates, ensuring isolated view rendering. It allows
20
 * changing of the rendering options such as layout during composing of the particular message without
21
 * affecting other messages.
22
 *
23
 * An instance of this class serves as a view context during the mail template rendering and is available
24
 * inside a view template file via [[\yii\base\View::context]].
25
 *
26
 * @see BaseMailer::compose()
27
 */
28
class Template implements ViewContextInterface
29
{
30
    /**
31
     * @var MessageInterface related mail message instance.
32
     */
33
    private MessageInterface $message;
34
35
    /**
36
     * @var View view instance used for rendering.
37
     */
38
    private View $view;
39
40
    /**
41
     * @var string path to the directory containing view files.
42
     */
43
    private string $viewPath;
44
45
    /**
46
     * @var string|array name of the view to use as a template. The value could be:
47
     *
48
     * - a string that contains either a view name for rendering HTML body of the email.
49
     *   The text body in this case is generated by applying `strip_tags()` to the HTML body.
50
     * - an array with 'html' and/or 'text' elements. The 'html' element refers to a view name
51
     *   for rendering the HTML body, while 'text' element is for rendering the text body. For example,
52
     *   `['html' => 'contact-html', 'text' => 'contact-text']`.
53
     */
54
    private $viewName;
55
56
    /**
57
     * @var string HTML layout view name. It is the layout used to render HTML mail body.
58
     * The property can take the following values:
59
     *
60
     * - a relative view name: a view file relative to [[viewPath]], e.g., 'layouts/html'.
61
     * - an empty string: the layout is disabled.
62
     */
63
    private string $htmlLayout = '';
64
65
    /**
66
     * @var string text layout view name. This is the layout used to render TEXT mail body.
67
     * Please refer to [[htmlLayout]] for possible values that this property can take.
68
     */
69
    private string $textLayout = '';
70
71
    /**
72
     * @param View $view
73
     * @param string $viewPath
74
     * @param string|array $viewName
75
     */
76 10
    public function __construct(View $view, string $viewPath, $viewName)
77
    {
78 10
        $this->view = $view;
79 10
        $this->viewPath = $viewPath;
80 10
        $this->viewName = $viewName;
81 10
    }
82
83
    /**
84
     * Sets html layout.
85
     * @param string $layout
86
     */
87 6
    public function setHtmlLayout(string $layout): void
88
    {
89 6
        $this->htmlLayout = $layout;
90 6
    }
91
92
    /**
93
     * Sets text layout.
94
     * @param string $layout
95
     */
96 6
    public function setTextLayout(string $layout): void
97
    {
98 6
        $this->textLayout = $layout;
99 6
    }
100
101 7
    public function getViewPath(): string
102
    {
103 7
        return $this->viewPath;
104
    }
105
106
    /**
107
     * Composes the given mail message according to this template.
108
     * @param MessageInterface $message the message to be composed.
109
     * @param array $parameters the parameters (name-value pairs) that will be extracted and made available in the view
110
     * file.
111
     * @throws Throwable|ViewNotFoundException
112
     */
113 4
    public function compose(MessageInterface $message, $parameters = []): void
114
    {
115 4
        $this->message = $message;
116
117 4
        if (is_array($this->viewName)) {
118 2
            if (isset($this->viewName['html'])) {
119 2
                $html = $this->render($this->viewName['html'], $parameters, $this->htmlLayout);
120
            }
121 2
            if (isset($this->viewName['text'])) {
122 2
                $text = $this->render($this->viewName['text'], $parameters, $this->textLayout);
123
            }
124
        } else {
125 4
            $html = $this->render($this->viewName, $parameters, $this->htmlLayout);
126
        }
127
128 4
        if (isset($html)) {
129 4
            $this->message->setHtmlBody($html);
130
        }
131 4
        if (isset($text)) {
132 2
            $this->message->setTextBody($text);
133
        } elseif (isset($html)) {
134 4
            if (preg_match('~<body[^>]*>(.*?)</body>~is', $html, $match)) {
135 1
                $html = $match[1];
136
            }
137
            // remove style and script
138 4
            $html = preg_replace('~<((style|script))[^>]*>(.*?)</\1>~is', '', $html);
139
            // strip all HTML tags and decode HTML entities
140 4
            $text = html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5);
141
            // improve whitespace
142 4
            $text = preg_replace("~^[ \t]+~m", '', trim($text));
143 4
            $text = preg_replace('~\R\R+~mu', "\n\n", $text);
144 4
            $this->message->setTextBody($text);
145
        }
146 4
    }
147
148
    /**
149
     * Renders the view specified with optional parameters and layout.
150
     * The view will be rendered using the [[view]] component.
151
     * @param string $view a view name of the view file.
152
     * @param array $parameters the parameters (name-value pairs) that will be extracted and made available in the view file.
153
     * @param string $layout layout view name. If the value is empty, no layout will be applied.
154
     * @throws Throwable|ViewNotFoundException
155
     * @return string the rendering result.
156
     */
157 6
    public function render(string $view, array $parameters = [], string $layout = ''): string
158
    {
159 6
        $output = $this->view->render($view, $parameters, $this);
160 6
        if ($layout === '') {
161 5
            return $output;
162
        }
163 1
        return $this->view->render($layout, ['content' => $output], $this);
164
    }
165
}
166