Completed
Branch master (88f1de)
by Delete
02:20 queued 16s
created

Converter::convertWithIconv()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 3
dl 0
loc 13
rs 9.4285
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
        if (!is_string($string)) {
37
            throw InvalidArgumentException::getInstance('string', 'json', $string, 1478195990);
38
        }
39
        if (!is_string($fromEncoding)) {
40
            throw InvalidArgumentException::getInstance('string', 'fromEncoding', $fromEncoding, 1478195991);
41
        }
42
        if (!is_string($toEncoding)) {
43
            throw InvalidArgumentException::getInstance('string', 'toEncoding', $toEncoding, 1478195992);
44
        }
45
46
        // Try different conversion functions, ordered by speed
47
        if (
48
            ($convertedString = $this->convertWithIconv($string, $fromEncoding, $toEncoding)) === null &&
49
            ($convertedString = $this->convertWithUConverter($string, $fromEncoding, $toEncoding)) === null &&
50
            ($convertedString = $this->convertWithMultiByteString($string, $fromEncoding, $toEncoding)) === null
51
        ) {
52
            throw new ExtensionRequiredException(
53
                "The 'iconv', 'intl' or the 'mbstring' extension is required to convert the JSON encoding.",
54
                1478095252
55
            );
56
        }
57
58
        return $convertedString;
59
    }
60
61
    /**
62
     * Removes the byte order mark (BOM) from the JSON text. This is not allowed in JSON,
63
     * but may be ignored when parsing it.
64
     *
65
     * @param string $string
66
     *
67
     * @return string
68
     * @throws \Crossjoin\Json\Exception\InvalidArgumentException
69
     */
70
    public function removeByteOrderMark($string)
71
    {
72
        // Check arguments
73
        if (!is_string($string)) {
74
            throw InvalidArgumentException::getInstance('string', 'string', $string, 1478195910);
75
        }
76
77
        return (string)preg_replace(
78
            '/^(?:' .
79
            '\xEF\xBB\xBF|' .     // UTF-8 BOM
80
            '\x00\x00\xFE\xFF|' . // UTF-32BE BOM
81
            '\xFF\xFE\x00\x00|' . // UTF-32LE BOM (before UTF-16LE check!)
82
            '\xFE\xFF|' .         // UTF-16BE BOM
83
            '\xFF\xFE' .          // UTF-16LE BOM
84
            ')/',
85
            '',
86
            $string
87
        );
88
    }
89
90
    /**
91
     * @return NativeJsonErrorException
92
     */
93
    protected function getNativeJsonErrorException()
94
    {
95
        if (function_exists('\json_last_error_msg')) {
96
            return new NativeJsonErrorException(\json_last_error_msg(), \json_last_error());
97
        } else {
98
            return new NativeJsonErrorException('An error occurred while encoding/decoding JSON.', \json_last_error());
99
        }
100
    }
101
102
    /**
103
     * @param $string
104
     * @param $fromEncoding
105
     * @param $toEncoding
106
     *
107
     * @return string|null
108
     * @throws \Crossjoin\Json\Exception\ConversionFailedException
109
     */
110
    private function convertWithIconv($string, $fromEncoding, $toEncoding)
111
    {
112
        // @codeCoverageIgnoreStart
113
        if (function_exists('iconv')) {
114
            $string = iconv($fromEncoding, $toEncoding . '//IGNORE', $string);
115
            if ($string === false) {
116
                throw new ConversionFailedException('Error while converting the encoding.', 1478193725);
117
            }
118
            return $string;
119
        }
120
        return null;
121
        // @codeCoverageIgnoreEnd
122
    }
123
124
    /**
125
     * @param $string
126
     * @param $fromEncoding
127
     * @param $toEncoding
128
     *
129
     * @return string|null
130
     */
131
    private function convertWithUConverter($string, $fromEncoding, $toEncoding)
132
    {
133
        // @codeCoverageIgnoreStart
134
        if (class_exists('\\UConverter')) {
135
            /** @noinspection PhpUndefinedClassInspection */
136
            $uConverter = new \UConverter($toEncoding, $fromEncoding);
137
            /** @noinspection PhpUndefinedMethodInspection */
138
            return $uConverter->convert($string);
139
        }
140
        return null;
141
        // @codeCoverageIgnoreEnd
142
    }
143
144
    /**
145
     * @param $string
146
     * @param $fromEncoding
147
     * @param $toEncoding
148
     *
149
     * @return string|null
150
     */
151
    private function convertWithMultiByteString($string, $fromEncoding, $toEncoding)
152
    {
153
        // @codeCoverageIgnoreStart
154
        if (function_exists('mb_convert_encoding')) {
155
            return mb_convert_encoding($string, $toEncoding, $fromEncoding);
156
        }
157
        return null;
158
        // @codeCoverageIgnoreEnd
159
    }
160
}
161