Issues (76)

src/Header/Part/ContainerPart.php (1 issue)

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\Header\Part;
9
10
use Psr\Log\LoggerInterface;
11
use ZBateson\MailMimeParser\ErrorBag;
12
use ZBateson\MbWrapper\MbWrapper;
13
14
/**
15
 * Base HeaderPart for a part that consists of other parts.
16
 *
17
 * The base container part constructs a string value out of the passed parts by
18
 * concatenating their values, discarding whitespace between parts that can be
19
 * ignored (in general allows for a single space but removes extras.)
20
 *
21
 * A ContainerPart can also contain any number of child comment parts.  The
22
 * CommentParts in this and all child parts can be returned by calling
23
 * getComments.
24
 *
25
 * @author Zaahid Bateson
26
 */
27
class ContainerPart extends HeaderPart
28
{
29
    /**
30
     * @var HeaderPart[] parts that were used to create this part, collected for
31
     *      proper error reporting and validation.
32
     */
33
    protected $children = [];
34
35 113
    public function __construct(
36
        LoggerInterface $logger,
37
        MbWrapper $charsetConverter,
38
        array $children
39
    ) {
40 113
        ErrorBag::__construct($logger);
41 113
        $this->charsetConverter = $charsetConverter;
42 113
        $this->children = $children;
43 113
        $str = (!empty($children)) ? $this->getValueFromParts($children) : '';
44 113
        parent::__construct(
45 113
            $logger,
46 113
            $this->charsetConverter,
47 113
            $str
48 113
        );
49
    }
50
51
    /**
52
     * Filters out ignorable space tokens.
53
     *
54
     * Spaces are removed if parts on either side of it have their
55
     * canIgnoreSpaceAfter/canIgnoreSpaceBefore properties set to true.
56
     *
57
     * @param HeaderPart[] $parts
58
     * @return HeaderPart[]
59
     */
60 113
    protected function filterIgnoredSpaces(array $parts) : array
61
    {
62 113
        $ends = (object) ['isSpace' => true, 'canIgnoreSpacesAfter' => true, 'canIgnoreSpacesBefore' => true, 'value' => ''];
63
64 113
        $spaced = \array_merge($parts, [$ends]);
65 113
        $filtered = \array_slice(\array_reduce(
66 113
            \array_slice(\array_keys($spaced), 0, -1),
67 113
            function($carry, $key) use ($spaced, $ends) {
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
68 113
                $p = $spaced[$key];
69 113
                $l = \end($carry);
70 113
                $a = $spaced[$key + 1];
71 113
                if ($p->isSpace && $a === $ends) {
72
                    // trim
73 100
                    if ($l->isSpace) {
74 18
                        \array_pop($carry);
75
                    }
76 100
                    return $carry;
77 113
                } elseif ($p->isSpace && ($l->isSpace || ($l->canIgnoreSpacesAfter && $a->canIgnoreSpacesBefore))) {
78 107
                    return $carry;
79
                }
80 113
                return \array_merge($carry, [$p]);
81 113
            },
82 113
            [$ends]
83 113
        ), 1);
84 113
        return $filtered;
85
    }
86
87
    /**
88
     * Creates the string value representation of this part constructed from the
89
     * child parts passed to it.
90
     *
91
     * The default implementation filters out ignorable whitespace between
92
     * parts, and concatenates parts calling 'getValue'.
93
     *
94
     * @param HeaderParts[] $parts
95
     */
96 113
    protected function getValueFromParts(array $parts) : string
97
    {
98 113
        return \array_reduce($this->filterIgnoredSpaces($parts), fn ($c, $p) => $c . $p->getValue(), '');
99
    }
100
101
    /**
102
     * Returns the child parts this container part consists of.
103
     *
104
     * @return IHeaderPart[]
105
     */
106 2
    public function getChildParts() : array
107
    {
108 2
        return $this->children;
109
    }
110
111 1
    public function getComments() : array
112
    {
113 1
        return \array_merge(...\array_filter(\array_map(
114 1
            fn ($p) => ($p instanceof CommentPart) ? [$p] : $p->getComments(),
115 1
            $this->children
116 1
        )));
117
    }
118
119
    /**
120
     * Returns this part's children, same as getChildParts().
121
     *
122
     * @return ErrorBag
123
     */
124 2
    protected function getErrorBagChildren() : array
125
    {
126 2
        return $this->children;
127
    }
128
}
129