Converter::getNativeJsonErrorException()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Crossjoin\Json;
3
4
use Crossjoin\Json\Exception\ConversionFailedException;
5
use Crossjoin\Json\Exception\ExtensionRequiredException;
6
use Crossjoin\Json\Exception\InvalidArgumentException;
7
use Crossjoin\Json\Exception\NativeJsonErrorException;
8
9
/**
10
 * Class Converter
11
 *
12
 * @package Crossjoin\Json
13
 * @author Christoph Ziegenberg <[email protected]>
14
 */
15
abstract class Converter
16
{
17
    const UTF8    = 'UTF-8';
18
    const UTF16BE = 'UTF-16BE';
19
    const UTF16LE = 'UTF-16LE';
20
    const UTF32BE = 'UTF-32BE';
21
    const UTF32LE = 'UTF-32LE';
22
23
    /**
24
     * @param string $string
25
     * @param string $fromEncoding
26
     * @param string $toEncoding
27
     *
28
     * @return string
29
     * @throws \Crossjoin\Json\Exception\InvalidArgumentException
30
     * @throws \Crossjoin\Json\Exception\ConversionFailedException
31
     * @throws \Crossjoin\Json\Exception\ExtensionRequiredException
32
     */
33
    public function convertEncoding($string, $fromEncoding, $toEncoding)
34
    {
35
        // Check arguments
36
        InvalidArgumentException::validateArgument(InvalidArgumentException::TYPE_STRING, 'json', $string, 1478195990);
37
        InvalidArgumentException::validateArgument(InvalidArgumentException::TYPE_STRING, 'fromEncoding', $fromEncoding, 1478195991);
38
        InvalidArgumentException::validateArgument(InvalidArgumentException::TYPE_STRING, 'toEncoding', $toEncoding, 1478195992);
39
40
        if ($fromEncoding === $toEncoding) {
41
            return $string;
42
        }
43
44
        return $this->tryConvertEncoding($string, $fromEncoding, $toEncoding);
45
    }
46
47
    /**
48
     * @param $string
49
     * @param $fromEncoding
50
     * @param $toEncoding
51
     *
52
     * @return string
53
     * @throws \Crossjoin\Json\Exception\ExtensionRequiredException
54
     * @throws \Crossjoin\Json\Exception\ConversionFailedException
55
     */
56
    private function tryConvertEncoding($string, $fromEncoding, $toEncoding)
57
    {
58
        // Try different conversion functions, ordered by speed
59
        if (($converted = $this->convertWithIconv($string, $fromEncoding, $toEncoding)) !== null) {
60
            return $converted;
61
        } elseif (($converted = $this->convertWithUConverter($string, $fromEncoding, $toEncoding)) !== null) {
62
            return $converted;
63
        } elseif (($converted = $this->convertWithMultiByteString($string, $fromEncoding, $toEncoding)) !== null) {
64
            return $converted;
65
        }
66
67
        // No available method found
68
        throw new ExtensionRequiredException(
69
            "The 'iconv', 'intl' or the 'mbstring' extension is required to convert the JSON encoding.",
70
            1478095252
71
        );
72
    }
73
74
    /**
75
     * Removes the byte order mark (BOM) from the JSON text. This is not allowed in JSON,
76
     * but may be ignored when parsing it.
77
     *
78
     * @param string $string
79
     *
80
     * @return string
81
     * @throws \Crossjoin\Json\Exception\InvalidArgumentException
82
     */
83
    public function removeByteOrderMark($string)
84
    {
85
        // Check arguments
86
        InvalidArgumentException::validateArgument(InvalidArgumentException::TYPE_STRING, 'string', $string, 1478195910);
87
88
        return (string)preg_replace(
89
            '/^(?:' .
90
            '\xEF\xBB\xBF|' .     // UTF-8 BOM
91
            '\x00\x00\xFE\xFF|' . // UTF-32BE BOM
92
            '\xFF\xFE\x00\x00|' . // UTF-32LE BOM (before UTF-16LE check!)
93
            '\xFE\xFF|' .         // UTF-16BE BOM
94
            '\xFF\xFE' .          // UTF-16LE BOM
95
            ')/',
96
            '',
97
            $string
98
        );
99
    }
100
101
    /**
102
     * @return NativeJsonErrorException
103
     */
104
    protected function getNativeJsonErrorException()
105
    {
106
        // @codeCoverageIgnoreStart
107
        if (function_exists('\json_last_error_msg')) {
108
            return new NativeJsonErrorException(\json_last_error_msg(), \json_last_error());
109
        } else {
110
            return new NativeJsonErrorException('An error occurred while encoding/decoding JSON.', \json_last_error());
111
        }
112
        // @codeCoverageIgnoreEnd
113
    }
114
115
    /**
116
     * @param $string
117
     * @param $fromEncoding
118
     * @param $toEncoding
119
     *
120
     * @return string|null
121
     * @throws \Crossjoin\Json\Exception\ConversionFailedException
122
     */
123
    private function convertWithIconv($string, $fromEncoding, $toEncoding)
124
    {
125
        // @codeCoverageIgnoreStart
126
        if (function_exists('iconv')) {
127
            $string = iconv($fromEncoding, $toEncoding . '//IGNORE', $string);
128
            if ($string === false) {
129
                throw new ConversionFailedException('Error while converting the encoding.', 1478193725);
130
            }
131
            return $string;
132
        }
133
        return null;
134
        // @codeCoverageIgnoreEnd
135
    }
136
137
    /**
138
     * @param $string
139
     * @param $fromEncoding
140
     * @param $toEncoding
141
     *
142
     * @return string|null
143
     */
144
    private function convertWithUConverter($string, $fromEncoding, $toEncoding)
145
    {
146
        // @codeCoverageIgnoreStart
147
        if (class_exists('\\UConverter')) {
148
            /** @noinspection PhpUndefinedClassInspection */
149
            $uConverter = new \UConverter($toEncoding, $fromEncoding);
150
            /** @noinspection PhpUndefinedMethodInspection */
151
            return $uConverter->convert($string);
152
        }
153
        return null;
154
        // @codeCoverageIgnoreEnd
155
    }
156
157
    /**
158
     * @param $string
159
     * @param $fromEncoding
160
     * @param $toEncoding
161
     *
162
     * @return string|null
163
     */
164
    private function convertWithMultiByteString($string, $fromEncoding, $toEncoding)
165
    {
166
        // @codeCoverageIgnoreStart
167
        if (function_exists('mb_convert_encoding')) {
168
            return mb_convert_encoding($string, $toEncoding, $fromEncoding);
169
        }
170
        return null;
171
        // @codeCoverageIgnoreEnd
172
    }
173
}
174