Structure::detectParts()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 15
rs 9.6111
cc 5
nc 4
nop 3
1
<?php
2
/*
3
* File: Structure.php
4
* Category: -
5
* Author: M.Goldenbaum
6
* Created: 17.09.20 20:38
7
* Updated: -
8
*
9
* Description:
10
*  -
11
*/
12
13
namespace Webklex\PHPIMAP;
14
15
16
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
17
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
18
19
/**
20
 * Class Structure
21
 *
22
 * @package Webklex\PHPIMAP
23
 */
24
class Structure {
25
26
    /**
27
     * Raw structure
28
     *
29
     * @var string $raw
30
     */
31
    public $raw = "";
32
33
    /**
34
     * @var Header $header
35
     */
36
    private $header = null;
37
38
    /**
39
     * Message type (if multipart or not)
40
     *
41
     * @var int $type
42
     */
43
    public $type = IMAP::MESSAGE_TYPE_TEXT;
44
45
    /**
46
     * All available parts
47
     *
48
     * @var Part[] $parts
49
     */
50
    public $parts = [];
51
52
    /**
53
     * Config holder
54
     *
55
     * @var array $config
56
     */
57
    protected $config = [];
58
59
    /**
60
     * Structure constructor.
61
     * @param $raw_structure
62
     * @param Header $header
63
     *
64
     * @throws MessageContentFetchingException
65
     * @throws InvalidMessageDateException
66
     */
67
    public function __construct($raw_structure, Header $header) {
68
        $this->raw = $raw_structure;
69
        $this->header = $header;
70
        $this->config = ClientManager::get('options');
71
        $this->parse();
72
    }
73
74
    /**
75
     * Parse the given raw structure
76
     *
77
     * @throws MessageContentFetchingException
78
     * @throws InvalidMessageDateException
79
     */
80
    protected function parse(){
81
        $this->findContentType();
82
        $this->parts = $this->find_parts();
83
    }
84
85
    /**
86
     * Determine the message content type
87
     */
88
    public function findContentType(){
89
        $content_type = $this->header->get("content_type");
90
        $content_type = (is_array($content_type)) ? implode(' ', $content_type) : $content_type;
91
        if($content_type && stripos($content_type, 'multipart') === 0) {
92
            $this->type = IMAP::MESSAGE_TYPE_MULTIPART;
93
        }else{
94
            $this->type = IMAP::MESSAGE_TYPE_TEXT;
95
        }
96
    }
97
98
    /**
99
     * Find all available headers and return the left over body segment
100
     * @var string $context
101
     * @var integer $part_number
102
     *
103
     * @return Part[]
104
     * @throws InvalidMessageDateException
105
     */
106
    private function parsePart(string $context, int $part_number = 0): array {
107
        $body = $context;
108
        while (($pos = strpos($body, "\r\n")) > 0) {
109
            $body = substr($body, $pos + 2);
110
        }
111
        $headers = substr($context, 0, strlen($body) * -1);
112
        $body = substr($body, 0, -2);
113
114
        $headers = new Header($headers);
115
        if (($boundary = $headers->getBoundary()) !== null) {
116
            return $this->detectParts($boundary, $body, $part_number);
117
        }
118
        return [new Part($body, $headers, $part_number)];
119
    }
120
121
    /**
122
     * @param string $boundary
123
     * @param string $context
124
     * @param int $part_number
125
     *
126
     * @return array
127
     * @throws InvalidMessageDateException
128
     */
129
    private function detectParts(string $boundary, string $context, int $part_number = 0): array {
130
        $base_parts = explode( $boundary, $context);
131
        $final_parts = [];
132
        foreach($base_parts as $ctx) {
133
            $ctx = substr($ctx, 2);
134
            if ($ctx !== "--" && $ctx != "") {
135
                $parts = $this->parsePart($ctx, $part_number);
136
                foreach ($parts as $part) {
137
                    $final_parts[] = $part;
138
                    $part_number = $part->part_number;
139
                }
140
                $part_number++;
141
            }
142
        }
143
        return $final_parts;
144
    }
145
146
    /**
147
     * Find all available parts
148
     *
149
     * @return array
150
     * @throws MessageContentFetchingException
151
     * @throws InvalidMessageDateException
152
     */
153
    public function find_parts(): array {
154
        if($this->type === IMAP::MESSAGE_TYPE_MULTIPART) {
155
            if (($boundary = $this->header->getBoundary()) === null)  {
156
                throw new MessageContentFetchingException("no content found", 0);
157
            }
158
159
            return $this->detectParts($boundary, $this->raw);
160
        }
161
162
        return [new Part($this->raw, $this->header)];
163
    }
164
165
    /**
166
     * Try to find a boundary if possible
167
     *
168
     * @return string|null
169
     * @Depricated since version 2.4.4
170
     */
171
    public function getBoundary(){
172
        return $this->header->getBoundary();
173
    }
174
}
175