GenericHelper::replacePart()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 10
cc 2
nc 2
nop 3
crap 2
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\Message\Helper;
9
10
use ZBateson\MailMimeParser\Header\HeaderConsts;
11
use ZBateson\MailMimeParser\Header\IHeader;
12
use ZBateson\MailMimeParser\IMessage;
13
use ZBateson\MailMimeParser\MailMimeParser;
14
use ZBateson\MailMimeParser\Message\IMimePart;
15
16
/**
17
 * Provides common Message helper routines for Message manipulation.
18
 *
19
 * @author Zaahid Bateson
20
 */
21
class GenericHelper extends AbstractHelper
22
{
23
    /**
24
     * @var string[] non mime content fields that are not related to the content
25
     *      of a part.
26
     */
27
    private static array $nonMimeContentFields = ['contentreturn', 'contentidentifier'];
28
29
    /**
30
     * Returns true if the passed header's name is a Content-* header other than
31
     * one defined in the static $nonMimeContentFields
32
     *
33
     */
34 23
    private function isMimeContentField(IHeader $header, array $exceptions = []) : bool
35
    {
36 23
        return (\stripos($header->getName(), 'Content') === 0
37 23
            && !\in_array(\strtolower(\str_replace('-', '', $header->getName())), \array_merge(self::$nonMimeContentFields, $exceptions)));
38
    }
39
40
    /**
41
     * Copies the passed $header from $from, to $to or sets the header to
42
     * $default if it doesn't exist in $from.
43
     *
44
     * @param string $header
45
     * @param string $default
46
     */
47 23
    public function copyHeader(IMimePart $from, IMimePart $to, $header, $default = null) : static
48
    {
49 23
        $fromHeader = $from->getHeader($header);
50 23
        $set = ($fromHeader !== null) ? $fromHeader->getRawValue() : $default;
51 23
        if ($set !== null) {
52 23
            $to->setRawHeader($header, $set);
53
        }
54 23
        return $this;
55
    }
56
57
    /**
58
     * Removes Content-* headers from the passed part, then detaches its content
59
     * stream.
60
     *
61
     * An exception is made for the obsolete Content-Return header, which isn't
62
     * isn't a MIME content field and so isn't removed.
63
     */
64 22
    public function removeContentHeadersAndContent(IMimePart $part) : static
65
    {
66 22
        foreach ($part->getAllHeaders() as $header) {
67 22
            if ($this->isMimeContentField($header)) {
68 21
                $part->removeHeader($header->getName());
69
            }
70
        }
71 22
        $part->detachContentStream();
72 22
        return $this;
73
    }
74
75
    /**
76
     * Copies Content-* headers from the $from header into the $to header. If
77
     * the Content-Type header isn't defined in $from, defaults to text/plain
78
     * with utf-8 and quoted-printable as its Content-Transfer-Encoding.
79
     *
80
     * An exception is made for the obsolete Content-Return header, which isn't
81
     * isn't a MIME content field and so isn't copied.
82
     *
83
     * @param bool $move
84
     */
85 22
    public function copyContentHeadersAndContent(IMimePart $from, IMimePart $to, $move = false) : static
86
    {
87 22
        $this->copyHeader($from, $to, HeaderConsts::CONTENT_TYPE, 'text/plain; charset=utf-8');
88 22
        if ($from->getHeader(HeaderConsts::CONTENT_TYPE) === null) {
89 5
            $this->copyHeader($from, $to, HeaderConsts::CONTENT_TRANSFER_ENCODING, 'quoted-printable');
90
        } else {
91 18
            $this->copyHeader($from, $to, HeaderConsts::CONTENT_TRANSFER_ENCODING);
92
        }
93 22
        foreach ($from->getAllHeaders() as $header) {
94 22
            if ($this->isMimeContentField($header, ['contenttype', 'contenttransferencoding'])) {
95 4
                $this->copyHeader($from, $to, $header->getName());
96
            }
97
        }
98 22
        if ($from->hasContent()) {
99 19
            $to->attachContentStream($from->getContentStream(), MailMimeParser::DEFAULT_CHARSET);
0 ignored issues
show
Bug introduced by
It seems like $from->getContentStream() can also be of type null; however, parameter $stream of ZBateson\MailMimeParser\...::attachContentStream() does only seem to accept Psr\Http\Message\StreamInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

99
            $to->attachContentStream(/** @scrutinizer ignore-type */ $from->getContentStream(), MailMimeParser::DEFAULT_CHARSET);
Loading history...
100
        }
101 22
        if ($move) {
102 21
            $this->removeContentHeadersAndContent($from);
103
        }
104 22
        return $this;
105
    }
106
107
    /**
108
     * Creates a new content part from the passed part, allowing the part to be
109
     * used for something else (e.g. changing a non-mime message to a multipart
110
     * mime message).
111
     *
112
     * @return IMimePart the newly-created IMimePart
113
     */
114 10
    public function createNewContentPartFrom(IMimePart $part) : IMimePart
115
    {
116 10
        $mime = $this->mimePartFactory->newInstance();
117 10
        $this->copyContentHeadersAndContent($part, $mime, true);
118 10
        return $mime;
119
    }
120
121
    /**
122
     * Copies type headers (Content-Type, Content-Disposition,
123
     * Content-Transfer-Encoding) from the $from MimePart to $to.  Attaches the
124
     * content resource handle of $from to $to, and loops over child parts,
125
     * removing them from $from and adding them to $to.
126
     *
127
     */
128 13
    public function movePartContentAndChildren(IMimePart $from, IMimePart $to) : static
129
    {
130 13
        $this->copyContentHeadersAndContent($from, $to, true);
131 13
        if ($from->getChildCount() > 0) {
132 8
            foreach ($from->getChildIterator() as $child) {
133 8
                $from->removePart($child);
134 8
                $to->addChild($child);
135
            }
136
        }
137 13
        return $this;
138
    }
139
140
    /**
141
     * Replaces the $part IMimePart with $replacement.
142
     *
143
     * Essentially removes $part from its parent, and adds $replacement in its
144
     * same position.  If $part is the IMessage, then $part can't be removed and
145
     * replaced, and instead $replacement's type headers are copied to $message,
146
     * and any children below $replacement are added directly below $message.
147
     */
148 6
    public function replacePart(IMessage $message, IMimePart $part, IMimePart $replacement) : static
149
    {
150 6
        $position = $message->removePart($replacement);
151 6
        if ($part === $message) {
0 ignored issues
show
introduced by
The condition $part === $message is always false.
Loading history...
152 4
            $this->movePartContentAndChildren($replacement, $message);
153 4
            return $this;
154
        }
155 2
        $parent = $part->getParent();
156 2
        $parent->addChild($replacement, $position);
157 2
        $parent->removePart($part);
158
159 2
        return $this;
160
    }
161
}
162