Completed
Push — ezp26352-skip_csrf_check_on_re... ( 19f37a )
by
unknown
36:29
created

Generator::getStackPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the Generator base class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\REST\Common\Output;
12
13
/**
14
 * Output generator.
15
 */
16
abstract class Generator
17
{
18
    /**
19
     * Generator creation stack.
20
     *
21
     * Use to check if it is OK to start / close the requested element in the
22
     * current state.
23
     *
24
     * @var array
25
     */
26
    protected $stack = array();
27
28
    /**
29
     * If set to true, output will be formatted and indented.
30
     *
31
     * @var bool
32
     */
33
    protected $formatOutput = false;
34
35
    public function setFormatOutput($formatOutput)
36
    {
37
        $this->formatOutput = (bool)$formatOutput;
38
    }
39
40
    /**
41
     * Reset output visitor to a virgin state.
42
     */
43
    public function reset()
44
    {
45
        $this->stack = array();
46
        $this->isEmpty = true;
0 ignored issues
show
Bug introduced by
The property isEmpty does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
47
    }
48
49
    /**
50
     * Start document.
51
     *
52
     * @param mixed $data
53
     */
54
    abstract public function startDocument($data);
55
56
    /**
57
     * Returns if the document is empty or already contains data.
58
     *
59
     * @return bool
60
     */
61
    abstract public function isEmpty();
62
63
    /**
64
     * Check start document.
65
     *
66
     * @param mixed $data
67
     */
68
    protected function checkStartDocument($data)
69
    {
70
        if (count($this->stack)) {
71
            throw new Exceptions\OutputGeneratorException(
72
                'Starting a document may only be the very first operation.'
73
            );
74
        }
75
76
        $this->stack[] = array('document', $data, array());
77
    }
78
79
    /**
80
     * End document.
81
     *
82
     * Returns the generated document as a string.
83
     *
84
     * @param mixed $data
85
     *
86
     * @return string
87
     */
88
    abstract public function endDocument($data);
89
90
    /**
91
     * Check end document.
92
     *
93
     * @param mixed $data
94
     */
95
    protected function checkEndDocument($data)
96
    {
97
        $this->checkEnd('document', $data);
98
    }
99
100
    /**
101
     * Start object element.
102
     *
103
     * @param string $name
104
     * @param string $mediaTypeName
105
     */
106
    abstract public function startObjectElement($name, $mediaTypeName = null);
107
108
    /**
109
     * Check start object element.
110
     *
111
     * @param mixed $data
112
     */
113 View Code Duplication
    protected function checkStartObjectElement($data)
114
    {
115
        $this->checkStart('objectElement', $data, array('document', 'objectElement', 'hashElement', 'list'));
116
117
        $last = count($this->stack) - 2;
118
        if ($this->stack[$last][0] !== 'list') {
119
            // Ensure object element type only occurs once outside of lists
120
            if (isset($this->stack[$last][2][$data])) {
121
                throw new Exceptions\OutputGeneratorException(
122
                    "Element {$data} may only occur once inside of {$this->stack[$last][0]}."
123
                );
124
            }
125
        }
126
        $this->stack[$last][2][$data] = true;
127
    }
128
129
    /**
130
     * End object element.
131
     *
132
     * @param string $name
133
     */
134
    abstract public function endObjectElement($name);
135
136
    /**
137
     * Check end object element.
138
     *
139
     * @param mixed $data
140
     */
141
    protected function checkEndObjectElement($data)
142
    {
143
        $this->checkEnd('objectElement', $data);
144
    }
145
146
    /**
147
     * Start hash element.
148
     *
149
     * @param string $name
150
     */
151
    abstract public function startHashElement($name);
152
153
    /**
154
     * Check start hash element.
155
     *
156
     * @param mixed $data
157
     */
158 View Code Duplication
    protected function checkStartHashElement($data)
159
    {
160
        $this->checkStart('hashElement', $data, array('document', 'objectElement', 'hashElement', 'list'));
161
162
        $last = count($this->stack) - 2;
163
        if ($this->stack[$last][0] !== 'list') {
164
            // Ensure hash element type only occurs once outside of lists
165
            if (isset($this->stack[$last][2][$data])) {
166
                throw new Exceptions\OutputGeneratorException(
167
                    "Element {$data} may only occur once inside of {$this->stack[$last][0]}."
168
                );
169
            }
170
        }
171
        $this->stack[$last][2][$data] = true;
172
    }
173
174
    /**
175
     * End hash element.
176
     *
177
     * @param string $name
178
     */
179
    abstract public function endHashElement($name);
180
181
    /**
182
     * Check end hash element.
183
     *
184
     * @param mixed $data
185
     */
186
    protected function checkEndHashElement($data)
187
    {
188
        $this->checkEnd('hashElement', $data);
189
    }
190
191
    /**
192
     * Start value element.
193
     *
194
     * @param string $name
195
     * @param string $value
196
     */
197
    abstract public function startValueElement($name, $value);
198
199
    /**
200
     * Check start value element.
201
     *
202
     * @param mixed $data
203
     */
204
    protected function checkStartValueElement($data)
205
    {
206
        $this->checkStart('valueElement', $data, array('objectElement', 'hashElement', 'list'));
207
    }
208
209
    /**
210
     * End value element.
211
     *
212
     * @param string $name
213
     */
214
    abstract public function endValueElement($name);
215
216
    /**
217
     * Check end value element.
218
     *
219
     * @param mixed $data
220
     */
221
    protected function checkEndValueElement($data)
222
    {
223
        $this->checkEnd('valueElement', $data);
224
    }
225
226
    /**
227
     * Start list.
228
     *
229
     * @param string $name
230
     */
231
    abstract public function startList($name);
232
233
    /**
234
     * Check start list.
235
     *
236
     * @param mixed $data
237
     */
238
    protected function checkStartList($data)
239
    {
240
        $this->checkStart('list', $data, array('objectElement', 'hashElement'));
241
    }
242
243
    /**
244
     * End list.
245
     *
246
     * @param string $name
247
     */
248
    abstract public function endList($name);
249
250
    /**
251
     * Check end list.
252
     *
253
     * @param mixed $data
254
     */
255
    protected function checkEndList($data)
256
    {
257
        $this->checkEnd('list', $data);
258
    }
259
260
    /**
261
     * Start attribute.
262
     *
263
     * @param string $name
264
     * @param string $value
265
     */
266
    abstract public function startAttribute($name, $value);
267
268
    /**
269
     * Check start attribute.
270
     *
271
     * @param mixed $data
272
     */
273
    protected function checkStartAttribute($data)
274
    {
275
        $this->checkStart('attribute', $data, array('objectElement', 'hashElement'));
276
    }
277
278
    /**
279
     * End attribute.
280
     *
281
     * @param string $name
282
     */
283
    abstract public function endAttribute($name);
284
285
    /**
286
     * Check end attribute.
287
     *
288
     * @param mixed $data
289
     */
290
    protected function checkEndAttribute($data)
291
    {
292
        $this->checkEnd('attribute', $data);
293
    }
294
295
    /**
296
     * Get media type.
297
     *
298
     * @param string $name
299
     *
300
     * @return string
301
     */
302
    abstract public function getMediaType($name);
303
304
    /**
305
     * Generates a media type from $name and $type.
306
     *
307
     * @param string $name
308
     * @param string $type
309
     *
310
     * @return string
311
     */
312
    protected function generateMediaType($name, $type)
313
    {
314
        return "application/vnd.ez.api.{$name}+{$type}";
315
    }
316
317
    /**
318
     * Generates a generic representation of the scalar, hash or list given in
319
     * $hashValue into the document, using an element of $hashElementName as
320
     * its parent.
321
     *
322
     * @param string $hashElementName
323
     * @param mixed $hashValue
324
     */
325
    abstract public function generateFieldTypeHash($hashElementName, $hashValue);
326
327
    /**
328
     * Check close / end operation.
329
     *
330
     * @param string $type
331
     * @param mixed $data
332
     * @param array $validParents
333
     */
334
    protected function checkStart($type, $data, array $validParents)
335
    {
336
        $lastTag = end($this->stack);
337
338
        if (!is_array($lastTag)) {
339
            throw new Exceptions\OutputGeneratorException(
340
                sprintf(
341
                    'Invalid start: Trying to open outside of a document.'
342
                )
343
            );
344
        }
345
346
        if (!in_array($lastTag[0], $validParents)) {
347
            throw new Exceptions\OutputGeneratorException(
348
                sprintf(
349
                    'Invalid start: Trying to open %s inside %s, valid parent nodes are: %s.',
350
                    $type,
351
                    $lastTag[0],
352
                    implode(', ', $validParents)
353
                )
354
            );
355
        }
356
357
        $this->stack[] = array($type, $data, array());
358
    }
359
360
    /**
361
     * Check close / end operation.
362
     *
363
     * @param string $type
364
     * @param mixed $data
365
     */
366
    protected function checkEnd($type, $data)
367
    {
368
        $lastTag = array_pop($this->stack);
369
370
        if (!is_array($lastTag)) {
371
            throw new Exceptions\OutputGeneratorException(
372
                sprintf(
373
                    'Invalid close: Trying to close on empty stack.'
374
                )
375
            );
376
        }
377
378
        if (array($lastTag[0], $lastTag[1]) !== array($type, $data)) {
379
            throw new Exceptions\OutputGeneratorException(
380
                sprintf(
381
                    'Invalid close: Trying to close %s:%s, while last element was %s:%s.',
382
                    $type,
383
                    $data,
384
                    $lastTag[0],
385
                    $lastTag[1]
386
                )
387
            );
388
        }
389
    }
390
391
    /**
392
     * Serializes a boolean value.
393
     *
394
     * @param bool $boolValue
395
     *
396
     * @return mixed
397
     */
398
    abstract public function serializeBool($boolValue);
399
400
    /**
401
     * Returns a string representation of the current stack of the document.
402
     *
403
     * Example: "Location.ContentInfo'.
404
     * Elements of type 'attribute', 'document' and 'list' are ignored.
405
     *
406
     * @return string
407
     */
408
    public function getStackPath()
409
    {
410
        $relevantNodes = array_filter(
411
            $this->stack,
412
            function ($value) {
413
                return !in_array($value[0], ['attribute', 'document', 'list']);
414
            }
415
        );
416
417
        $names = array_map(
418
            function ($value) {
419
                return $value[1];
420
            },
421
            $relevantNodes
422
        );
423
424
        return implode('.', $names);
425
    }
426
}
427