Passed
Branch master (950424)
by Christopher
11:06
created

JsonWriter::getJsonOutput()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace POData\Writers\Json;
4
5
use Carbon\Carbon;
6
7
/**
8
 * Class JsonWriter.
9
 */
10
class JsonWriter
11
{
12
    /**
13
     * Json datetime format.
14
     */
15
    private $jsonDateTimeFormat = "\/Date(%s)\/";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \/Date(%s)\/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
introduced by
The private property $jsonDateTimeFormat is not used, and could be removed.
Loading history...
16
17
    /**
18
     * Writer to write text into.
19
     */
20
    private $writer;
21
22
    /**
23
     * scope of the json text - object, array, etc.
24
     */
25
    private $scopes = [];
26
27
    /**
28
     * Various scope types for Json writer.
29
     */
30
    private $scopeType = ['Array' => 0, 'Object' => 1];
31
32
    /**
33
     * Creates a new instance of Json writer.
34
     *
35
     * @param string $writer writer to which text needs to be written
36
     */
37
    public function __construct($writer)
38
    {
39
        $this->writer = new IndentedTextWriter($writer);
40
    }
41
42
    /**
43
     * End the current scope.
44
     *
45
     * @return JsonWriter
46
     */
47
    public function endScope()
48
    {
49
        $this->writer->writeLine()->decreaseIndent();
50
51
        if (array_pop($this->scopes)->type == $this->scopeType['Array']) {
52
            $this->writer->writeValue(']');
53
        } else {
54
            $this->writer->writeValue('}');
55
        }
56
57
        return $this;
58
    }
59
60
    /**
61
     * Start the array scope.
62
     *
63
     * @return JsonWriter
64
     */
65
    public function startArrayScope()
66
    {
67
        $this->startScope($this->scopeType['Array']);
68
69
        return $this;
70
    }
71
72
    /**
73
     * Start the object scope.
74
     *
75
     * @return JsonWriter
76
     */
77
    public function startObjectScope()
78
    {
79
        $this->startScope($this->scopeType['Object']);
80
81
        return $this;
82
    }
83
84
    /**
85
     * Write the name for the object property.
86
     *
87
     * @param string $name name of the object property
88
     *
89
     * @return JsonWriter
90
     */
91
    public function writeName($name)
92
    {
93
        $currentScope = end($this->scopes);
94
        if ($currentScope && $currentScope->type == $this->scopeType['Object']) {
95
            if ($currentScope->objectCount != 0) {
96
                $this->writer->writeTrimmed(', ');
97
            }
98
99
            ++$currentScope->objectCount;
100
        }
101
102
        $this->writeCore($name, true /*quotes*/);
103
        $this->writer->writeTrimmed(': ');
104
105
        return $this;
106
    }
107
108
    /**
109
     * JSON write a basic data type (string, number, boolean, null).
110
     *
111
     * @param mixed       $value value to be written
112
     * @param string|null $type  data type of the value
113
     *
114
     * @return JsonWriter
115
     */
116
    public function writeValue($value, $type = null)
117
    {
118
        switch ($type) {
119
            case 'Edm.Boolean':
120
            case 'Edm.Int16':
121
            case 'Edm.Int32':
122
            case 'Edm.Byte':
123
            case 'Edm.SByte':
124
                $this->writeCore($value, /* quotes */ false);
125
                break;
126
127
            case 'Edm.Int64':
128
            case 'Edm.Guid':
129
            case 'Edm.Decimal':
130
            case 'Edm.Binary':
131
                $this->writeCore($value, /* quotes */ true);
132
                break;
133
134
            case 'Edm.Single':
135
            case 'Edm.Double':
136
                if (is_infinite($value) || is_nan($value)) {
137
                    $this->writeCore('null', /* quotes */ true);
138
                } else {
139
                    $this->writeCore($value, /* quotes */ false);
140
                }
141
142
                break;
143
144
            case 'Edm.DateTime':
145
                $dateTime = new Carbon($value, new \DateTimeZone('UTC'));
146
                $formattedDateTime = $dateTime->format('U')*1000;
147
                $this->writeCore('/Date(' . $formattedDateTime . ')/', /* quotes */ true);
148
                break;
149
150
            case 'Edm.String':
151
                if ($value == null) {
152
                    $this->writeCore('null', /* quotes */ false);
153
                } else {
154
                    $jsonEncoded = json_encode($value);
155
                    //json_encode always escapes a solidus (forward slash, %x2F),
156
                    //this will be a problem when encoding urls
157
                    //JSON_UNESCAPED_SLASHES not available in earlier versions of php 5.3
158
                    //So removing escaping forward slashes manually
159
                    $jsonEncoded = str_replace('\\/', '/', $jsonEncoded);
160
                    //since json_encode is already appending chords
161
                    //there is no need to set it again
162
                    $this->writeCore($jsonEncoded, /* quotes */ false);
163
                }
164
                break;
165
166
            default:
167
                $this->writeCore($this->quoteJScriptString($value), /* quotes */ true);
168
        }
169
170
        return $this;
171
    }
172
173
    /**
174
     * Returns the string value with special characters escaped.
175
     *
176
     * @param string $string input string value
177
     *
178
     * Returns the string value with special characters escaped
179
     *
180
     * @return string
181
     */
182
    private function quoteJScriptString($string)
183
    {
184
        // Escape ( " \ / \n \r \t \b \f) characters with a backslash.
185
        $search = ['\\', "\n", "\t", "\r", "\b", "\f", '"'];
186
        $replace = ['\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'];
187
        $processedString = str_replace($search, $replace, $string);
188
        // Escape some ASCII characters - namely, 0x08 and 0x0c
189
        $processedString = str_replace([chr(0x08), chr(0x0C)], ['\b', '\f'], $processedString);
190
191
        return $processedString;
192
    }
193
194
    /**
195
     * Write the string value with/without quotes.
196
     *
197
     * @param string $text   value to be written
198
     * @param bool   $quotes put quotes around the value if this value is true
199
     */
200
    private function writeCore($text, $quotes)
201
    {
202 View Code Duplication
        if (0 != count($this->scopes)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
            $currentScope = end($this->scopes);
204
            if ($currentScope->type == $this->scopeType['Array']) {
205
                if (0 != $currentScope->objectCount) {
206
                    $this->writer->writeTrimmed(', ');
207
                }
208
209
                ++$currentScope->objectCount;
210
            }
211
        }
212
213
        if ($quotes && 'null' !== $text) {
214
            $this->writer->writeValue('"');
215
        }
216
217
        $this->writer->writeValue($text);
218
        if ($quotes && 'null' !== $text) {
219
            $this->writer->writeValue('"');
220
        }
221
    }
222
223
    /**
224
     * Start the scope given the scope type.
225
     *
226
     * @param int $type scope type
227
     */
228
    private function startScope($type)
229
    {
230 View Code Duplication
        if (0 != count($this->scopes)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
231
            $currentScope = end($this->scopes);
232
            if (($currentScope->type == $this->scopeType['Array']) && (0 != $currentScope->objectCount)) {
233
                $this->writer->writeTrimmed(', ');
234
            }
235
236
            ++$currentScope->objectCount;
237
        }
238
239
        $scope = new Scope($type);
240
        array_push($this->scopes, $scope);
241
242
        if ($type == $this->scopeType['Array']) {
243
            $this->writer->writeValue('[');
244
        } else {
245
            $this->writer->writeValue('{');
246
        }
247
248
        $this->writer->increaseIndent()->writeLine();
249
    }
250
251
    /**
252
     * return the indented result.
253
     *
254
     * @return string
255
     */
256
    public function getJsonOutput()
257
    {
258
        return $this->writer->getResult();
259
    }
260
}
261