VCardHelper::imageFromString()   B
last analyzed

Complexity

Conditions 10
Paths 17

Size

Total Lines 54
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 39
c 3
b 2
f 0
dl 0
loc 54
rs 7.6666
cc 10
nc 17
nop 2

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
declare(strict_types=1);
3
4
namespace SKien\VCard;
5
6
/**
7
 * Helper trait containing some methods used by multiple classes in package.
8
 *
9
 * @package VCard
10
 * @author Stefanius <[email protected]>
11
 * @copyright MIT License - see the LICENSE file for details
12
 * @internal
13
 */
14
trait VCardHelper
15
{
16
    /**
17
     * Build property to insert in vcard.
18
     * If line exceeds max length, data will be split into multiple lines
19
     * @param string    $strName
20
     * @param string    $strValue
21
     * @param bool      $bMask      have value to be masked (default: true)
22
     * @return string
23
     */
24
    protected function buildProperty(string $strName, string $strValue, bool $bMask = true) : string
25
    {
26
        $buffer = '';
27
        if (!empty($strValue)) {
28
            if ($bMask) {
29
                $strValue = $this->maskString($strValue);
30
            }
31
            $strLine = $strName . ':' . $strValue;
32
            if (strlen($strLine) > VCard::MAX_LINE_LENGTH) {
33
                $buffer = substr($strLine, 0, VCard::MAX_LINE_LENGTH) . PHP_EOL;
34
                $strLine = substr($strLine, VCard::MAX_LINE_LENGTH);
35
                $iMax = VCard::MAX_LINE_LENGTH - 1;
36
                while (strlen($strLine) > $iMax) {
37
                    $buffer .= ' ' . substr($strLine, 0, $iMax) . PHP_EOL;
38
                    $strLine = substr($strLine, $iMax);
39
                }
40
                $buffer .= ' ' . $strLine . PHP_EOL;
41
            } else {
42
                $buffer = $strLine . PHP_EOL;
43
            }
44
        }
45
        return $buffer;
46
    }
47
48
    /**
49
     * Mask delimiter and newline if inside of value.
50
     * @param string $strValue
51
     * @return string
52
     */
53
    protected function maskString(string $strValue) : string
54
    {
55
        // decode entities before ';' is replaced !!
56
        $strValue = html_entity_decode($strValue, ENT_HTML5);
57
        $strValue = str_replace("\r\n", "\n", $strValue);
58
        $strValue = str_replace("\r", "\n", $strValue);
59
        $strValue = str_replace("\n", "\\n", $strValue);
60
        $strValue = str_replace(",", "\\,", $strValue);
61
        $strValue = str_replace(";", "\\;", $strValue);
62
63
        $strFrom = mb_detect_encoding($strValue);
64
        if ($strFrom !== false && $strFrom != VCard::getEncoding()) {
65
            $strValue = iconv($strFrom, VCard::getEncoding(), $strValue);
66
            if ($strValue === false) {
67
                $strValue = '';
68
            }
69
        }
70
71
        return $strValue;
72
    }
73
74
    /**
75
     * Unmask delimiter and newline.
76
     * @param string $strValue
77
     * @return string
78
     */
79
    protected function unmaskString(string $strValue) : string
80
    {
81
        $strValue = str_replace("\\n", "\n", $strValue);
82
        $strValue = str_replace("\\,", ",", $strValue);
83
        $strValue = str_replace("\\;", ";", $strValue);
84
85
        $strFrom = mb_detect_encoding($strValue);
86
        if ($strFrom !== false && $strFrom != VCard::getEncoding()) {
87
            $strValue = iconv($strFrom, VCard::getEncoding() . "//IGNORE", $strValue);
88
            if ($strValue === false) {
89
                $strValue = '';
90
            }
91
        }
92
93
        return $strValue;
94
    }
95
96
    /**
97
     * Explode a masked string.
98
     * to ignore masked delimiters belonging to value
99
     * @param string $strDelim
100
     * @param string $strValue
101
     * @return array<string>
102
     */
103
    protected function explodeMaskedString(string $strDelim, string $strValue) : array
104
    {
105
        // save masked delimiters, tag unmasked, restore saved and explode on new taged delimiter
106
        $strSave = "\\" . $strDelim;
107
        $strValue = str_replace($strSave, "\x00", $strValue);
108
        $strValue = str_replace($strDelim, "\x01", $strValue);
109
        $strValue = str_replace("\x00", $strSave, $strValue);
110
111
        $a = explode("\x01", $strValue);
112
        return $a == false ? [] : $a;
113
    }
114
115
    /**
116
     * Parse image date (base64) to extract type and raw data.
117
     * @param string $blobImage
118
     * @param string $strType
119
     * @param string $strImage
120
     */
121
    protected function parseImageData(string $blobImage, string &$strType, string &$strImage) : void
122
    {
123
        // extract image type from binary data (e.g. data:image/jpg;base64,)
124
        $i = strpos($blobImage, ',');
125
        if ($i > 0) {
126
            $strType = substr($blobImage, 0, $i);
127
            $iFrom = strpos($strType, '/');
128
            $iTo = strpos($strType, ';');
129
            $strType = strtoupper(substr($strType, $iFrom + 1, $iTo - $iFrom - 1));
130
            $strImage = substr($blobImage, $i + 1);
131
        }
132
    }
133
134
    /**
135
     * Parse param string
136
     * @param array<string> $aParamsIn
137
     * @return array<string,string>
138
     */
139
    protected function parseParams(array $aParamsIn) : array
140
    {
141
        $aParams = array();
142
        $iCount = count($aParamsIn);
143
        for ($i = 1; $i < $iCount; $i++) {
144
            $aSplit = explode('=', $aParamsIn[$i]);
145
            if (count($aSplit) < 2) {
146
                // version 2.1 allows paramvalues without paramname
147
                $strName = $this->paramName($aSplit[0]);
148
                $strValue = strtoupper($aSplit[0]);
149
            } else {
150
                $strName = strtoupper($aSplit[0]);
151
                $strValue = strtoupper($aSplit[1]);
152
            }
153
            if (isset($aParams[$strName])) {
154
                $aParams[$strName] .= ',' . $strValue;
155
            } else {
156
                $aParams[$strName] = $strValue;
157
            }
158
        }
159
        return $aParams;
160
    }
161
162
    /**
163
     * Find paramname to paramvalue.
164
     * vcard version 2.1 allows params without name of the param
165
     * e.g.    TEL;HOME: short for TEL;TYPE=HOME:
166
     * @param string $strValue
167
     * @return string
168
     */
169
    protected function paramName(string $strValue) : string
170
    {
171
        static $aNames = array(
172
            'INLINE'            => 'VALUE',
173
            'URI'               => 'VALUE',
174
            'URL'               => 'VALUE',
175
            'CID'               => 'VALUE',
176
            '7BIT'              => 'ENCODING',
177
            'QUOTED-PRINTABLE'  => 'ENCODING',
178
            'BASE64'            => 'ENCODING',
179
            'DOM'               => 'TYPE',
180
            'INTL'              => 'TYPE',
181
            'POSTAL'            => 'TYPE',
182
            'PARCEL'            => 'TYPE',
183
            'HOME'              => 'TYPE',
184
            'WORK'              => 'TYPE',
185
            'PREF'              => 'TYPE',
186
            'VOICE'             => 'TYPE',
187
            'FAX'               => 'TYPE',
188
            'MSG'               => 'TYPE',
189
            'CELL'              => 'TYPE',
190
            'PAGER'             => 'TYPE',
191
            'BBS'               => 'TYPE',
192
            'MODEM'             => 'TYPE',
193
            'CAR'               => 'TYPE',
194
            'ISDN'              => 'TYPE',
195
            'VIDEO'             => 'TYPE',
196
            'ATTMAIL'           => 'TYPE',
197
            'CIS'               => 'TYPE',
198
            'EWORLD'            => 'TYPE',
199
            'INTERNET'          => 'TYPE',
200
            'PRODIGY'           => 'TYPE',
201
            'TLX'               => 'TYPE',
202
            'X400'              => 'TYPE',
203
            'GIF'               => 'TYPE',
204
            'CGM'               => 'TYPE',
205
            'WMF'               => 'TYPE',
206
            'BMP'               => 'TYPE',
207
            'MET'               => 'TYPE',
208
            'PMB'               => 'TYPE',
209
            'DIB'               => 'TYPE',
210
            'PICT'              => 'TYPE',
211
            'TIFF'              => 'TYPE',
212
            'PDF'               => 'TYPE',
213
            'PS'                => 'TYPE',
214
            'JPEG'              => 'TYPE',
215
            'QTIME'             => 'TYPE',
216
            'MPEG'              => 'TYPE',
217
            'MPEG2'             => 'TYPE',
218
            'AVI'               => 'TYPE',
219
            'WAVE'              => 'TYPE',
220
            'PCM'               => 'TYPE'
221
        );
222
223
        $strName = 'UNKNOWN';
224
        if (isset($aNames[$strValue])) {
225
            $strName = $aNames[$strValue];
226
        }
227
        return $strName;
228
    }
229
230
    /**
231
     * Create image ressource from encoded string.
232
     * Special processing for BMP cause there is no support in PHP before 7.2.
233
     * @param string    $strImage   base64 encoded image
234
     * @param string    $strType    image types supported by imagecreatefromstring
235
     * @return \GdImage|false    image resource
236
     */
237
    protected function imageFromString(string $strImage, string $strType)
238
    {
239
        $strImage = base64_decode($strImage);
240
        if ($strType != 'BMP') {
241
            $img = imagecreatefromstring($strImage);
242
        } else {
243
            // imagecreatefromstring don't supports BMP
244
            // ...imagecreatefrombmp() is available from PHP version >= 7.2!
245
            //
246
            // thanks to Tomáš Grasl for following code from
247
            // https://gist.github.com/freema/df8e7bae83c0e2a50ea4
248
            $temp = unpack("H*", $strImage);
249
            $hex = ($temp !== false ? $temp[1] : 0);
250
            $header = substr($hex, 0, 108);
251
            $width = 0;
252
            $height = 0;
253
            if (substr($header, 0, 4) == "424d") {
254
                $parts = str_split($header, 2);
255
                $width = (int)hexdec($parts[19] . $parts[18]);
256
                $height = (int)hexdec($parts[23] . $parts[22]);
257
                unset($parts);
258
            }
259
            $x = 0;
260
            $y = 1;
261
            $img = imagecreatetruecolor($width, $height);
262
            if ($img !== false) {
263
                $body = substr($hex, 108);
264
                $body_size = (strlen($body) / 2);
265
                $header_size = ($width * $height);
266
                $usePadding = ($body_size > ($header_size * 3) + 4);
267
                for ($i = 0; $i < $body_size; $i += 3) {
268
                    if ($x >= $width) {
269
                        if ($usePadding) {
270
                            $i += $width % 4;
271
                        }
272
                        $x = 0;
273
                        $y++;
274
                        if ($y > $height) {
275
                            break;
276
                        }
277
                    }
278
                    $i_pos = $i * 2;
279
                    $r = (int)hexdec($body[$i_pos + 4] . $body[$i_pos + 5]);
280
                    $g = (int)hexdec($body[$i_pos + 2] . $body[$i_pos + 3]);
281
                    $b = (int)hexdec($body[$i_pos] . $body[$i_pos + 1]);
282
                    $color = imagecolorallocate($img, $r, $g, $b);
283
                    if ($color !== false) {
284
                        imagesetpixel($img, $x, $height - $y, $color);
285
                    }
286
                    $x++;
287
                }
288
            }
289
        }
290
        return $img;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $img also could return the type resource which is incompatible with the documented return type GdImage|false.
Loading history...
291
    }
292
}