Content   F
last analyzed

Complexity

Total Complexity 82

Size/Duplication

Total Lines 543
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 82
lcom 0
cbo 0
dl 0
loc 543
ccs 140
cts 140
cp 1
rs 2
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A header() 0 10 4
A img() 0 8 2
A ownVideo() 0 8 3
A externalVideo() 0 12 2
A gallery() 0 10 3
A slider() 0 11 2
A share() 0 8 2
A rating() 0 11 4
B button() 0 21 8
A comment() 0 6 1
A accordion() 0 13 4
A adBlockPosition() 0 4 1
A additionalContent() 0 11 3
B inlineCallbackForm() 0 13 7
B modalCallbackForm() 0 19 11
A searchInput() 0 6 2
A generateSliderItemsBlock() 0 22 5
B generateCommentBlock() 0 30 8
A generateMenu() 0 10 2
B generateAdditionalContentItemsList() 0 24 8

How to fix   Complexity   

Complex Class

Complex classes like Content often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Content, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace sokolnikov911\YandexTurboPages\helpers;
4
5
use sokolnikov911\YandexTurboPages\Channel;
6
7
class Content
8
{
9
    const SHARE_TYPE_FACEBOOK = 'facebook';
10
    const SHARE_TYPE_GOOGLE = 'google';
11
    const SHARE_TYPE_ODNOKLASSNIKI = 'odnoklassniki';
12
    const SHARE_TYPE_TELEGRAM = 'telegram';
13
    const SHARE_TYPE_TWITTER = 'twitter';
14
    const SHARE_TYPE_VKONTAKTE = 'vkontakte';
15
16
    const SLIDER_DATA_VIEW_SQUARE = 'square';
17
    const SLIDER_DATA_VIEW_PORTRAIT = 'portrait';
18
    const SLIDER_DATA_VIEW_LANDSCAPE = 'landscape';
19
20
    const SLIDER_DATA_ITEM_VIEW_COVER = 'cover';
21
    const SLIDER_DATA_ITEM_VIEW_CONTAIN = 'contain';
22
23
    const OWN_VIDEO_TYPE_MP4 = 'video/mp4';
24
25
    const ADDITIONAL_CONTENT_ORIENTATION_VERTICAL = 'vertical';
26
    const ADDITIONAL_CONTENT_ORIENTATION_HORIZONTAL = 'horizontal';
27
28
    const ADDITIONAL_CONTENT_THUMB_POSITION_LEFT = 'left';
29
    const ADDITIONAL_CONTENT_THUMB_POSITION_RIGHT = 'right';
30
    const ADDITIONAL_CONTENT_THUMB_POSITION_TOP = 'top';
31
32
    const ADDITIONAL_CONTENT_THUMB_RATIO_1_1 = '1x1';
33
    const ADDITIONAL_CONTENT_THUMB_RATIO_2_3 = '2x3';
34
    const ADDITIONAL_CONTENT_THUMB_RATIO_3_2 = '3x2';
35
    const ADDITIONAL_CONTENT_THUMB_RATIO_3_4 = '3x4';
36
    const ADDITIONAL_CONTENT_THUMB_RATIO_4_3 = '4x3';
37
    const ADDITIONAL_CONTENT_THUMB_RATIO_16_9 = '16x9';
38
    const ADDITIONAL_CONTENT_THUMB_RATIO_16_10 = '16x10';
39
40
41
    /**
42
     * Generate header element
43
     * @param string $h1
44
     * @param string|null $h2
45
     * @param string|null $imgUrl
46
     * @param string|null $imgCaption
47
     * @param array|null $menuArray array of arrays with pairs of url and content
48
     * [
49
     *     ['url' => 'http://example/page1.html', 'title' => 'Page title 1'],
50
     *     ['url' => 'http://example/page2.html', 'title' => 'Page title 2'],
51
     * ]
52
     * @return string
53
     */
54 12
    public static function header(string $h1, string $h2 = null, string $imgUrl = null,
55
                                  string $imgCaption = null, array $menuArray = null): string
56
    {
57 12
        $header = '<h1>' . $h1 . '</h1>';
58 12
        $header .= $h2 ? '<h2>' . $h2 . '</h2>' : '';
59 12
        $header .= $menuArray ? self::generateMenu($menuArray) : '';
60 12
        $header .= $imgUrl ? self::img($imgUrl, $imgCaption) : '';
61
62 12
        return '<header>' . $header . '</header>';
63
    }
64
65
    /**
66
     * Generate image element
67
     * @param string $imgUrl
68
     * @param string|null $imgCaption
69
     * @return string
70
     */
71 12
    public static function img(string $imgUrl, string $imgCaption = null): string
72
    {
73 12
        $imageString = '<img src="' . $imgUrl . '" />';
74
75 12
        $imageString .= $imgCaption ? '<figcaption>' . $imgCaption . '</figcaption>' : '';
76
77 12
        return '<figure>' . $imageString . '</figure>';
78
    }
79
80
    /**
81
     * Generate video element
82
     * @param string $videoUrl
83
     * @param string|null $videoCaption
84
     * @param string $imgUrl
85
     * @param string $type
86
     * @return string
87
     */
88 8
    public static function ownVideo(string $videoUrl, string $videoCaption = null, string $type = self::OWN_VIDEO_TYPE_MP4, string $imgUrl = null): string
89
    {
90 8
        $videoString = '<video><source src="' . $videoUrl . '" type="' . $type . '" /></video>';
91 8
        $videoString .= $imgUrl ? '<img src="' . $imgUrl . '" />' : '';
92 8
        $videoString .= $videoCaption ? '<figcaption>' . $videoCaption . '</figcaption>' : '';
93
94 8
        return '<figure>' . $videoString . '</figure>';
95
    }
96
97
    /**
98
     * Generate video element for external video
99
     * @param string $videoUrl
100
     * @param array $options Options Array with next variables: width, height, frameborder, allowfullscreen,
101
     * referrerpolicy, sandbox, hd. Options example:
102
     * [
103
     *  'width' => 640,
104
     *  'height' => 480,
105
     *  'frameborder' => 1,
106
     *  'allowfullscreen' => 'true',
107
     *  'referrerpolicy' => 'unsafe-url',
108
     *  'sandbox' => 'allow-forms allow-modals',
109
     *  'hd' => 3
110
     * ]
111
     * @return string
112
     */
113 8
    public static function externalVideo(string $videoUrl, array $options = []): string
114
    {
115 8
        $videoString = '<iframe src="' . $videoUrl . '"';
116
117 8
        foreach ($options as $key => $value) {
118 4
            $videoString .= ' ' . $key . '="' . $value . '"';
119
        }
120
121 8
        $videoString .= '></iframe>';
122
123 8
        return $videoString;
124
    }
125
126
    /**
127
     * Generate images gallery
128
     * @param array $imagesArray Array of images urls
129
     * ['http://example.com/image1.jpg', 'http://example.com/image2.jpg']
130
     * @param string|null $header
131
     * @return string
132
     */
133 8
    public static function gallery(array $imagesArray, string $header = null): string
134
    {
135 8
        $galleryString = $header ? '<header>' . $header . '</header>' : '';
136
137 8
        foreach ($imagesArray as $image) {
138 8
            $galleryString .= '<img src="' . $image . '" />';
139
        }
140
141 8
        return '<div data-block="gallery">' . $galleryString . '</div>';
142
    }
143
144
    /**
145
     * Generate media slider
146
     * @param array $itemsArray Array of items with data
147
     * [
148
     *     ['url' => 'http://example.com/image1.jpg', 'title' => 'Image title 1', 'link' => ''],
149
     *     ['url' => 'http://example.com/image2.jpg', 'title' => 'Image title 2', 'link' => ''],
150
     *     ['url' => 'http://example.com/image3.jpg'],
151
     *     ['href' => 'http://example.com/page1.html', 'title' => 'Link title 1', 'text' => 'Link text 1']
152
     * ]
153
     * @param string|null $header
154
     * @param string $dataView
155
     * @param string $dataItemView
156
     * @return string
157
     */
158 8
    public static function slider(array $itemsArray, string $header = null,
159
                                  string $dataView = self::SLIDER_DATA_VIEW_SQUARE,
160
                                  string $dataItemView = self::SLIDER_DATA_ITEM_VIEW_COVER): string
161
    {
162 8
        $sliderString = $header ? '<header>' . $header . '</header>' : '';
163
164 8
        $sliderString .= self::generateSliderItemsBlock($itemsArray);
165
166 8
        return '<div data-block="slider" data-view="' . $dataView . '" data-item-view="'
167 8
            . $dataItemView . '">' . $sliderString . '</div>';
168
    }
169
170
    /**
171
     * Generate share block
172
     * @param array|null $networks Array of network names
173
     * [Content::SHARE_TYPE_GOOGLE, Content::SHARE_TYPE_TWITTER]
174
     * Can be empty, in this way all possible network types will be showed.
175
     * @return string
176
     */
177 8
    public static function share(array $networks = null): string
178
    {
179 8
        $networksString = $networks
180 4
            ? 'data-network="' . implode(',', $networks) . '"'
181 8
            : '';
182
183 8
        return '<div data-block="share" ' . $networksString . '></div>';
184
    }
185
186
    /**
187
     * Generate rating block
188
     * @param float $currentRating
189
     * @param float $maxRating
190
     * @return string
191
     */
192 16
    public static function rating(float $currentRating, float $maxRating): string
193
    {
194 16
        if (($currentRating > $maxRating) || ($maxRating <= 0) || ($currentRating < 0)) {
195 12
            throw new \UnexpectedValueException("Current rating can't be bigger than max value. And max value must be bigger than 0.");
196
        }
197
198
        return '<div itemscope="" itemtype="http://schema.org/Rating">
199 4
                       <meta itemprop="ratingValue" content="' . $currentRating . '" />
200 4
                       <meta itemprop="bestRating" content="' . $maxRating . '" />
201
                </div>';
202
    }
203
204
    /**
205
     * Generate button
206
     * @param string $text
207
     * @param string $url
208
     * @param string $phone Phone number in RFC-3966 format
209
     * @param string|null $buttonColor Can be Text or HEX
210
     * @param string|null $textColor Can be Text or HEX
211
     * @param bool $isBoldText
212
     * @param bool $isDisabled
213
     * @return string
214
     */
215 16
    public static function button(string $text, string $url = '', string $phone = '',
216
                                  string $buttonColor = null, string $textColor = null,
217
                                  bool $isBoldText = false, bool $isDisabled = false): string
218
    {
219 16
        if (!$url && !$phone) {
220 4
            throw new \UnexpectedValueException('Please set url or phone number for button');
221
        }
222
223 12
        $formAction = $url ? $url : 'tel:' . $phone;
224 12
        $buttonColorString = $buttonColor ? 'data-background-color="' . $buttonColor . '"' : '';
225 12
        $textColorString = $textColor ? 'data-color="' . $textColor . '"' : '';
226 12
        $isBoldTextString = $isBoldText ? 'data-primary="true"' : '';
227 12
        $isDisabledString = $isDisabled ? 'disabled="true"' : '';
228
229
        return "<button
230 12
                    formaction=\"" . $formAction . "\"
231 12
                    " . $buttonColorString . "
232 12
                    " . $textColorString . "
233 12
                    " . $isBoldTextString . "
234 12
                    " . $isDisabledString . ">" . $text . "</button>";
235
    }
236
237
    /**
238
     * Generate comment block
239
     * @param string $url URL to comments page
240
     * @param array $commentsArray multidimensional or one-dimensional array of comments,
241
     * can has unlimited includes, example:
242
     * [
243
     *  [
244
     *      'author' => 'First Author Name',
245
     *      'avatar' => 'http://example.com/user1.jpg',
246
     *      'title' => 'Comment Title',
247
     *      'subtitle' => '2017-12-10',
248
     *      'content' => 'Somme comment text',
249
     *      'comments' => [
250
     *          [
251
     *              'author' => 'Third Author Name',
252
     *              'avatar' => 'http://example.com/user3.jpg',
253
     *              'title' => 'Comment Title',
254
     *              'subtitle' => '2017-12-12',
255
     *              'content' => 'Some answer text'
256
     *          ],
257
     *          [
258
     *              'author' => 'Another Author Name',
259
     *              'avatar' => 'http://example.com/user4.jpg',
260
     *              'title' => 'Comment Title',
261
     *              'subtitle' => '2017-12-13',
262
     *              'content' => 'Another answer text'
263
     *          ],
264
     *      ]
265
     *  ],
266
     *  [
267
     *      'author' => 'Second Author Name',
268
     *      'avatar' => 'http://example.com/user2.jpg',
269
     *      'title' => 'Comment Title',
270
     *      'subtitle' => '2017-12-11',
271
     *      'content' => 'Some comment text'
272
     *  ],
273
     * ]
274
     * @return string
275
     */
276 4
    public static function comment(string $url, array $commentsArray): string
277
    {
278 4
        $commentBlock = self::generateCommentBlock($commentsArray);
279
280 4
        return '<div data-block="comments" data-url="' . $url . '">' . $commentBlock . '</div>';
281
    }
282
283
    /**
284
     * Generate accordion
285
     * @param array $accordionArray array accordion elements
286
     * [
287
     *     ['title' => 'Page title 1', 'text' => 'Text 1'],
288
     *     ['title' => 'Page title 2', 'text' => 'Text 2', 'expanded' => true],
289
     * ]
290
     * @return string
291
     */
292 4
    public static function accordion(array $accordionArray): string
293
    {
294 4
        $accordionString = '<div data-block="accordion">';
295
296 4
        foreach ($accordionArray as $item) {
297 4
            $expanded = isset($item['expanded']) && $item['expanded'] ? ' data-expanded="true"' : '';
298 4
            $accordionString .= '<div data-block="item" data-title="' . $item['title'] . '"' . $expanded . '>' . $item['text'] . '</div>';
299
        }
300
301 4
        $accordionString .= '</div>';
302
303 4
        return $accordionString;
304
    }
305
306
    /**
307
     * Generate Ad block position element
308
     * @param string $turboAdId value of $turboAdId used in Channel() class
309
     * @return string
310
     *
311
     * @see Channel::$adTurboAdId
312
     */
313 4
    public static function adBlockPosition(string $turboAdId): string
314
    {
315 4
        return '<figure data-turbo-ad-id="' . $turboAdId . '"></figure>';
316
    }
317
318
    /**
319
     * Generate block with additional content
320
     * @param array $itemsArray Array of items with data
321
     * [
322
     *     [
323
     *          'href' => 'http://example.com/page1.html',
324
     *          'title' => 'Item title 1',
325
     *          'description' => 'Item description',
326
     *          'thumb' => 'http://example/image1.jpg',
327
     *          'thumb_position' => Content::ADDITIONAL_CONTENT_THUMB_POSITION_LEFT,
328
     *          'thumb_ratio' => Content::ADDITIONAL_CONTENT_THUMB_RATIO_1_1
329
     *     ],
330
     *     [
331
     *          'href' => 'http://example.com/page2.html',
332
     *          'title' => 'Item title 2'
333
     *     ],
334
     * ]
335
     * @param string|null $title
336
     * @param string|null $orientation
337
     * @return string
338
     * @throws \Exception
339
     */
340 16
    public static function additionalContent(array $itemsArray, string $title = null, string $orientation = null): string
341
    {
342 16
        $contentString = '<div data-block="feed"';
343
344 16
        $contentString .= $orientation ? ' data-layout="' . $orientation . '"' : '';
345 16
        $contentString .= $title ? ' data-title="' . $title . '"' : '';
346
347 16
        $contentString .= '>';
348
349 16
        return $contentString . self::generateAdditionalContentItemsList($itemsArray) . '</div>';
350
    }
351
352
    /**
353
     * Generate inline callback form
354
     * @param string $recipientEmail
355
     * @param string|null $companyName
356
     * @param string|null $linkToAgreement
357
     * @return string
358
     * @throws \Exception
359
     */
360 16
    public static function inlineCallbackForm(string $recipientEmail, string $companyName = null, string $linkToAgreement = null): string
361
    {
362 16
        if ((empty($companyName) && !empty($linkToAgreement)) || (!empty($companyName) && empty($linkToAgreement))) {
363 8
            throw new \Exception("You should use both 'companyName' and 'linkToAgreement' or nothing");
364
        }
365
366 8
        $contentString = '';
367
368 8
        $contentString .= $companyName ? ' data-agreement-company="' . $companyName . '"' : '';
369 8
        $contentString .= $linkToAgreement ? ' data-agreement-link="' . $linkToAgreement . '"' : '';
370
371 8
        return '<form data-type="callback" data-send-to="' . $recipientEmail . '" ' . $contentString . '></form>';
372
    }
373
374
    /**
375
     * Generate modal callback form
376
     * @param string $recipientEmail
377
     * @param string $buttonText
378
     * @param string|null $companyName
379
     * @param string|null $linkToAgreement
380
     * @param string|null $buttonColor Can be Text or HEX
381
     * @param string|null $textColor Can be Text or HEX
382
     * @param bool $isBoldText
383
     * @param bool $isDisabled
384
     * @return string
385
     * @throws \Exception
386
     */
387 16
    public static function modalCallbackForm(string $recipientEmail, string $buttonText, string $companyName = null, string $linkToAgreement = null,
388
                                             string $buttonColor = null, string $textColor = null,
389
                                             bool $isBoldText = false, bool $isDisabled = false): string
390
    {
391 16
        if ((empty($companyName) && !empty($linkToAgreement)) || (!empty($companyName) && empty($linkToAgreement))) {
392 8
            throw new \Exception("You should use both 'companyName' and 'linkToAgreement' or nothing");
393
        }
394
395 8
        $contentString = '';
396
397 8
        $contentString .= $companyName ? ' data-agreement-company="' . $companyName . '"' : '';
398 8
        $contentString .= $linkToAgreement ? ' data-agreement-link="' . $linkToAgreement . '"' : '';
399 8
        $contentString .= $buttonColor ? ' data-background-color="' . $buttonColor . '"' : '';
400 8
        $contentString .= $textColor ? ' data-color="' . $textColor . '"' : '';
401 8
        $contentString .= $isBoldText ? ' data-primary="true"' : '';
402 8
        $contentString .= $isDisabled ? ' disabled="true"' : '';
403
404 8
        return '<button data-send-to="' . $recipientEmail . '" ' . $contentString . '>' . $buttonText . '</button>';
405
    }
406
407
    /**
408
     * Generate search input
409
     * @param string $searchUrl Search engine URL in format https://example.com/search/{text}
410
     * @param string $placeholder
411
     * @return string
412
     */
413 8
    public static function searchInput(string $searchUrl, string $placeholder = ''): string
414
    {
415 8
        $placeholder = $placeholder ? 'placeholder="' . $placeholder . '"'  : '';
416
417 8
        return '<form action="' . $searchUrl . '" method="GET"><input type="search" name="text" ' . $placeholder . '/></form>';
418
    }
419
420
    /**
421
     * Generate content block for media slider
422
     * @param array $itemsArray Array of items with data
423
     * [
424
     *     ['url' => 'http://example.com/image1.jpg', 'title' => 'Image title 1', 'link' => ''],
425
     *     ['url' => 'http://example.com/image2.jpg', 'title' => 'Image title 2', 'link' => ''],
426
     *     ['url' => 'http://example.com/image3.jpg'],
427
     *     ['href' => 'http://example.com/page1.html', 'title' => 'Link title 1', 'text' => 'Link text 1']
428
     * ]
429
     * @return string
430
     */
431 8
    private static function generateSliderItemsBlock(array $itemsArray): string
432
    {
433 8
        $sliderString = '';
434
435 8
        foreach ($itemsArray as $item) {
436 8
            $sliderString .= '<figure>';
437
438 8
            if (isset($item['title'])) {
439 8
                $sliderString .= '<figcaption>' . $item['title'] . '</figcaption>';
440
            }
441
442 8
            if (isset($item['url'])) {
443 8
                $sliderString .= '<img src="' . $item['url'] . '" />';
444 8
            } elseif (isset($item['href'])) {
445 8
                $sliderString .= '<a href="' . $item['href'] . '">' . $item['text'] . '</a>';
446
            }
447
448 8
            $sliderString .= '</figure>';
449
        }
450
451 8
        return $sliderString;
452
    }
453
454 4
    private static function generateCommentBlock(array $commentsArray): string
455
    {
456 4
        $commentBlock = '';
457
458 4
        foreach ($commentsArray as $commentArray) {
459 4
            $author = isset($commentArray['author']) ? 'data-author="' . $commentArray['author'] . '"' : '';
460 4
            $avatar = isset($commentArray['avatar']) ? 'data-avatar-url="' . $commentArray['avatar'] . '"' : '';
461 4
            $subtitle = isset($commentArray['subtitle']) ? 'data-subtitle="' . $commentArray['subtitle'] . '"' : '';
462
463
            $commentBlock .= '<div
464
                        data-block="comment"
465 4
                        ' . $author . ' 
466 4
                        ' . $avatar . '
467 4
                        ' . $subtitle . '                         
468
                        ><div data-block="content">';
469
470 4
            $commentBlock .= isset($commentArray['title']) ? '<header>' . $commentArray['title'] . '</header>' : '';
471 4
            $commentBlock .= isset($commentArray['content']) ? '<p>' . $commentArray['content'] . '</p></div>' : '';
472
473 4
            if (isset($commentArray['comments'])) {
474 4
                $commentBlock .= '<div data-block="comments">';
475 4
                $commentBlock .= self::generateCommentBlock($commentArray['comments']);
476 4
                $commentBlock .= '</div>';
477
            }
478
479 4
            $commentBlock .= '</div>';
480
        }
481
482 4
        return $commentBlock;
483
    }
484
485
    /**
486
     * Generate header menu
487
     * @param array $menuArray array of arrays with pairs of url and title
488
     * [
489
     *     ['url' => 'http://example.com/page1.html', 'title' => 'Page title 1'],
490
     *     ['url' => 'http://example.com/page2.html', 'title' => 'Page title 2'],
491
     * ]
492
     * @return string
493
     */
494 4
    private static function generateMenu(array $menuArray): string
495
    {
496 4
        $menuString = '';
497
498 4
        foreach ($menuArray as $menuItem) {
499 4
            $menuString .= '<a href="' . $menuItem['url'] . '">' . $menuItem['title'] . '</a>';
500
        }
501
502 4
        return '<menu>' . $menuString . '</menu>';
503
    }
504
505
    /**
506
     * Generate additional content items list
507
     * @param array $itemsArray array of arrays with data of additional content items
508
     * [
509
     *     [
510
     *          'href' => 'http://example.com/page1.html',
511
     *          'title' => 'Item title 1',
512
     *          'description' => 'Item description',
513
     *          'thumb' => 'http://example/image1.jpg',
514
     *          'thumb_position' => Content::ADDITIONAL_CONTENT_THUMB_POSITION_LEFT,
515
     *          'thumb_ratio' => Content::ADDITIONAL_CONTENT_THUMB_RATIO_1_1
516
     *     ],
517
     *     [
518
     *          'href' => 'http://example.com/page2.html',
519
     *          'title' => 'Item title 2'
520
     *     ],
521
     * ]
522
     * @return string
523
     * @throws \Exception
524
     */
525 16
    private static function generateAdditionalContentItemsList(array $itemsArray): string
526
    {
527 16
        $itemsString = '';
528
529 16
        foreach ($itemsArray as $item) {
530
531 16
            if (!isset($item['href']) || !isset($item['title'])) {
532 8
                throw new \Exception("Title and Url attributes are required");
533
            }
534
535
            $itemsString .= '<div data-block="feed-item"
536 8
                data-href="' . $item['href'] . '"
537 8
                data-title="' . $item['title'] . '"';
538
539 8
            $itemsString .= isset($item['description']) ? ' data-description="' . $item['description'] . '"' : '';
540 8
            $itemsString .= isset($item['thumb']) ? ' data-thumb="' . $item['thumb'] . '"' : '';
541 8
            $itemsString .= isset($item['thumb_position']) ? ' data-thumb-position="' . $item['thumb_position'] . '"' : '';
542 8
            $itemsString .= isset($item['thumb_ratio']) ? ' data-thumb-ratio="' . $item['thumb_ratio'] . '"' : '';
543
544 8
            $itemsString .= '/>';
545
        }
546
547 8
        return $itemsString;
548
    }
549
}