Completed
Pull Request — master (#203)
by
unknown
01:54
created

CliMenuBuilder::setWidth()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpSchool\CliMenu\Builder;
4
5
use Closure;
6
use PhpSchool\CliMenu\Action\ExitAction;
7
use PhpSchool\CliMenu\Action\GoBackAction;
8
use PhpSchool\CliMenu\Exception\InvalidShortcutException;
9
use PhpSchool\CliMenu\MenuItem\AsciiArtItem;
10
use PhpSchool\CliMenu\MenuItem\CheckableItem;
11
use PhpSchool\CliMenu\MenuItem\ItemStyleInterface;
12
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
13
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
14
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
15
use PhpSchool\CliMenu\MenuItem\RadioItem;
16
use PhpSchool\CliMenu\MenuItem\SelectableInterface;
17
use PhpSchool\CliMenu\MenuItem\SelectableItem;
18
use PhpSchool\CliMenu\CliMenu;
19
use PhpSchool\CliMenu\MenuItem\SplitItem;
20
use PhpSchool\CliMenu\MenuItem\StaticItem;
21
use PhpSchool\CliMenu\MenuStyle;
22
use PhpSchool\CliMenu\Style\CheckableStyle;
23
use PhpSchool\CliMenu\Style\RadioStyle;
24
use PhpSchool\CliMenu\Style\SelectableStyle;
25
use PhpSchool\CliMenu\Terminal\TerminalFactory;
26
use PhpSchool\Terminal\Terminal;
27
28
/**
29
 * @author Michael Woodward <[email protected]>
30
 * @author Aydin Hassan <[email protected]>
31
 */
32
class CliMenuBuilder
33
{
34
    /**
35
     * @var CliMenu
36
     */
37
    private $menu;
38
39
    /**
40
     * @var string
41
     */
42
    private $goBackButtonText = 'Go Back';
43
44
    /**
45
     * @var string
46
     */
47
    private $exitButtonText = 'Exit';
48
49
    /**
50
     * @var MenuStyle
51
     */
52
    private $style;
53
54
    /**
55
     * @var Terminal
56
     */
57
    private $terminal;
58
59
    /**
60
     * @var bool
61
     */
62
    private $disableDefaultItems = false;
63
64
    /**
65
     * @var bool
66
     */
67
    private $disabled = false;
68
69
    /**
70
     * Whether or not to auto create keyboard shortcuts for items
71
     * when they contain square brackets. Eg: [M]y item
72
     *
73
     * @var bool
74
     */
75
    private $autoShortcuts = false;
76
77
    /**
78
     * Regex to auto match for shortcuts defaults to looking
79
     * for a single character encased in square brackets
80
     *
81
     * @var string
82
     */
83
    private $autoShortcutsRegex = '/\[(.)\]/';
84
85
    /**
86
     * @var bool
87
     */
88
    private $subMenu = false;
89
90
    public function __construct(Terminal $terminal = null)
91
    {
92
        $this->terminal = $terminal ?? TerminalFactory::fromSystem();
93
        $this->style    = new MenuStyle($this->terminal);
94
        $this->menu     = new CliMenu(null, [], $this->terminal, $this->style);
95
    }
96
    
97
    public static function newSubMenu(Terminal $terminal) : self
98
    {
99
        $instance = new self($terminal);
100
        $instance->subMenu = true;
101
        
102
        return $instance;
103
    }
104
105
    public function setTitle(string $title) : self
106
    {
107
        $this->menu->setTitle($title);
108
109
        return $this;
110
    }
111
112
    public function addMenuItem(MenuItemInterface $item) : self
113
    {
114
        $this->menu->addItem($item);
115
116
        $this->processItemShortcut($item);
117
118
        return $this;
119
    }
120
121
    public function addItem(
122
        string $text,
123
        callable $itemCallable,
124
        bool $showItemExtra = false,
125
        bool $disabled = false
126
    ) : self {
127
        $item = (new SelectableItem($text, $itemCallable, $showItemExtra, $disabled))
128
            ->setStyle($this->menu->getSelectableStyle());
129
130
        $this->addMenuItem($item);
131
132
        return $this;
133
    }
134
135
    public function addItems(array $items) : self
136
    {
137
        foreach ($items as $item) {
138
            $this->addItem(...$item);
0 ignored issues
show
Bug introduced by
The call to PhpSchool\CliMenu\Builde...iMenuBuilder::addItem() has too few arguments starting with itemCallable. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

138
            $this->/** @scrutinizer ignore-call */ 
139
                   addItem(...$item);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
139
        }
140
141
        return $this;
142
    }
143
144
    public function addCheckableItem(
145
        string $text,
146
        callable $itemCallable,
147
        bool $showItemExtra = false,
148
        bool $disabled = false
149
    ) : self {
150
        $item = (new CheckableItem($text, $itemCallable, $showItemExtra, $disabled))
151
            ->setStyle($this->menu->getCheckableStyle());
152
153
        $this->addMenuItem($item);
154
155
        return $this;
156
    }
157
158
    public function addRadioItem(
159
        string $text,
160
        callable $itemCallable,
161
        bool $showItemExtra = false,
162
        bool $disabled = false
163
    ) : self {
164
        $item = (new RadioItem($text, $itemCallable, $showItemExtra, $disabled))
165
            ->setStyle($this->menu->getRadioStyle());
166
167
        $this->addMenuItem($item);
168
169
        return $this;
170
    }
171
172
    public function addStaticItem(string $text) : self
173
    {
174
        $this->addMenuItem(new StaticItem($text));
175
176
        return $this;
177
    }
178
179
    public function addLineBreak(string $breakChar = ' ', int $lines = 1) : self
180
    {
181
        $this->addMenuItem(new LineBreakItem($breakChar, $lines));
182
183
        return $this;
184
    }
185
186
    public function addAsciiArt(string $art, string $position = AsciiArtItem::POSITION_CENTER, string $alt = '') : self
187
    {
188
        $this->addMenuItem(new AsciiArtItem($art, $position, $alt));
189
190
        return $this;
191
    }
192
193
    public function addSubMenu(string $text, Closure $callback) : self
194
    {
195
        $builder = self::newSubMenu($this->terminal);
196
197
        if ($this->autoShortcuts) {
198
            $builder->enableAutoShortcuts($this->autoShortcutsRegex);
199
        }
200
201
        $callback($builder);
202
203
        $menu = $this->createMenuClosure($builder);
204
205
        $item = (new MenuMenuItem($text, $menu, $builder->isMenuDisabled()))
206
            ->setStyle($this->menu->getSelectableStyle());
207
208
        $this->menu->addItem($item);
209
210
        $this->processItemShortcut($item);
211
212
        return $this;
213
    }
214
215
    public function addSubMenuFromBuilder(string $text, CliMenuBuilder $builder) : self
216
    {
217
        $menu = $this->createMenuClosure($builder);
218
219
        $item = (new MenuMenuItem($text, $menu, $builder->isMenuDisabled()))
220
            ->setStyle($this->menu->getSelectableStyle());
221
222
        $this->menu->addItem($item);
223
224
        $this->processItemShortcut($item);
225
226
        return $this;
227
    }
228
229
    /**
230
     * Create the submenu as a closure which is then unpacked in MenuMenuItem::showSubMenu
231
     * This allows us to wait until all user-provided styles are parsed and apply them to nested items
232
     *
233
     * @param CliMenuBuilder|SplitItemBuilder $builder
234
     * @return Closure
235
     */
236
    protected function createMenuClosure($builder) : Closure
237
    {
238
        return function () use ($builder) {
239
            $menu = $builder->build();
240
241
            $menu->setParent($this->menu);
0 ignored issues
show
Bug introduced by
The method setParent() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

241
            $menu->/** @scrutinizer ignore-call */ 
242
                   setParent($this->menu);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
242
243
            // we apply the parent theme if nothing was changed
244
            // if no styles were changed in this sub-menu
245
            if (!$menu->getStyle()->hasChangedFromDefaults()) {
0 ignored issues
show
Bug introduced by
The method getStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

245
            if (!$menu->/** @scrutinizer ignore-call */ getStyle()->hasChangedFromDefaults()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
246
                $menu->setStyle($this->menu->getStyle());
0 ignored issues
show
Bug introduced by
The method setStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

246
                $menu->/** @scrutinizer ignore-call */ 
247
                       setStyle($this->menu->getStyle());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
247
            }
248
249
            $menu->setCheckableStyle(function (CheckableStyle $style) {
0 ignored issues
show
Bug introduced by
The method setCheckableStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

249
            $menu->/** @scrutinizer ignore-call */ 
250
                   setCheckableStyle(function (CheckableStyle $style) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
250
                $style->fromArray($this->menu->getCheckableStyle()->toArray());
251
            });
252
253
            $menu->setRadioStyle(function (RadioStyle $style) {
0 ignored issues
show
Bug introduced by
The method setRadioStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

253
            $menu->/** @scrutinizer ignore-call */ 
254
                   setRadioStyle(function (RadioStyle $style) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
254
                $style->fromArray($this->menu->getRadioStyle()->toArray());
255
            });
256
257
            $menu->setSelectableStyle(function (SelectableStyle $style) {
0 ignored issues
show
Bug introduced by
The method setSelectableStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

257
            $menu->/** @scrutinizer ignore-call */ 
258
                   setSelectableStyle(function (SelectableStyle $style) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
258
                $style->fromArray($this->menu->getSelectableStyle()->toArray());
259
            });
260
261
            // This will be filled with user-provided items
262
            foreach ($menu->getItems() as $item) {
263
                // Only set style for compatible items
264
                if (!$item instanceof ItemStyleInterface) {
265
                    continue;
266
                }
267
268
                // If item has a custom style, skip overriding
269
                if ($item->getStyle()->getIsCustom()) {
270
                    continue;
271
                }
272
273
                if ($item instanceof CheckableStyle) {
274
                    $item->setStyle(clone $menu->getCheckableStyle());
0 ignored issues
show
Bug introduced by
The method getCheckableStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

274
                    $item->setStyle(clone $menu->/** @scrutinizer ignore-call */ getCheckableStyle());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
275
                }
276
277
                if ($item instanceof RadioStyle) {
278
                    $item->setStyle(clone $menu->getRadioStyle());
0 ignored issues
show
Bug introduced by
The method setStyle() does not exist on PhpSchool\CliMenu\Style\RadioStyle. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

278
                    $item->/** @scrutinizer ignore-call */ 
279
                           setStyle(clone $menu->getRadioStyle());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getRadioStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

278
                    $item->setStyle(clone $menu->/** @scrutinizer ignore-call */ getRadioStyle());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
279
                }
280
281
                if ($item instanceof SelectableInterface) {
282
                    $item->setStyle(clone $menu->getSelectableStyle());
0 ignored issues
show
Bug introduced by
The method getSelectableStyle() does not exist on PhpSchool\CliMenu\MenuItem\SplitItem. Did you maybe mean getSelectedItem()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

282
                    $item->setStyle(clone $menu->/** @scrutinizer ignore-call */ getSelectableStyle());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
283
                }
284
            }
285
286
            return $menu;
287
        };
288
    }
289
290
    public function enableAutoShortcuts(string $regex = null) : self
291
    {
292
        $this->autoShortcuts = true;
293
294
        if (null !== $regex) {
295
            $this->autoShortcutsRegex = $regex;
296
        }
297
298
        return $this;
299
    }
300
301
    private function extractShortcut(string $title) : ?string
302
    {
303
        preg_match($this->autoShortcutsRegex, $title, $match);
304
305
        if (!isset($match[1])) {
306
            return null;
307
        }
308
309
        if (mb_strlen($match[1]) > 1) {
310
            throw InvalidShortcutException::fromShortcut($match[1]);
311
        }
312
313
        return isset($match[1]) ? strtolower($match[1]) : null;
314
    }
315
316
    private function processItemShortcut(MenuItemInterface $item) : void
317
    {
318
        $this->processIndividualShortcut($item, function (CliMenu $menu) use ($item) {
319
            $menu->executeAsSelected($item);
320
        });
321
    }
322
323
    private function processSplitItemShortcuts(SplitItem $splitItem) : void
324
    {
325
        foreach ($splitItem->getItems() as $item) {
326
            $this->processIndividualShortcut($item, function (CliMenu $menu) use ($splitItem, $item) {
327
                $current = $splitItem->getSelectedItemIndex();
328
329
                $splitItem->setSelectedItemIndex(
330
                    array_search($item, $splitItem->getItems(), true)
0 ignored issues
show
Bug introduced by
It seems like array_search($item, $splitItem->getItems(), true) can also be of type false and string; however, parameter $index of PhpSchool\CliMenu\MenuIt...:setSelectedItemIndex() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

330
                    /** @scrutinizer ignore-type */ array_search($item, $splitItem->getItems(), true)
Loading history...
331
                );
332
333
                $menu->executeAsSelected($splitItem);
334
335
                if ($current !== null) {
336
                    $splitItem->setSelectedItemIndex($current);
337
                }
338
            });
339
        }
340
    }
341
342
    private function processIndividualShortcut(MenuItemInterface $item, callable $callback) : void
343
    {
344
        if (!$this->autoShortcuts) {
345
            return;
346
        }
347
348
        if ($shortcut = $this->extractShortcut($item->getText())) {
349
            $this->menu->addCustomControlMapping(
350
                $shortcut,
351
                $callback
352
            );
353
        }
354
    }
355
356
    public function addSplitItem(Closure $callback) : self
357
    {
358
        $builder = new SplitItemBuilder($this->menu);
359
360
        if ($this->autoShortcuts) {
361
            $builder->enableAutoShortcuts($this->autoShortcutsRegex);
362
        }
363
364
        $builder->setCheckableStyle(function (CheckableStyle $style) {
365
            $style->fromArray($this->menu->getCheckableStyle()->toArray());
366
        });
367
368
        $builder->setRadioStyle(function (RadioStyle $style) {
369
            $style->fromArray($this->menu->getRadioStyle()->toArray());
370
        });
371
372
        $builder->setSelectableStyle(function (SelectableStyle $style) {
373
            $style->fromArray($this->menu->getSelectableStyle()->toArray());
374
        });
375
376
        $callback($builder);
377
        
378
        $this->menu->addItem($splitItem = $builder->build());
379
380
        $this->processSplitItemShortcuts($splitItem);
381
382
        return $this;
383
    }
384
385
    /**
386
     * Disable a submenu
387
     *
388
     * @throws \InvalidArgumentException
389
     */
390
    public function disableMenu() : self
391
    {
392
        if (!$this->subMenu) {
393
            throw new \InvalidArgumentException(
394
                'You can\'t disable the root menu'
395
            );
396
        }
397
398
        $this->disabled = true;
399
400
        return $this;
401
    }
402
403
    public function isMenuDisabled() : bool
404
    {
405
        return $this->disabled;
406
    }
407
408
    public function setGoBackButtonText(string $goBackButtonTest) : self
409
    {
410
        $this->goBackButtonText = $goBackButtonTest;
411
412
        return $this;
413
    }
414
415
    public function setExitButtonText(string $exitButtonText) : self
416
    {
417
        $this->exitButtonText = $exitButtonText;
418
419
        return $this;
420
    }
421
422
    public function setBackgroundColour(string $colour, string $fallback = null) : self
423
    {
424
        $this->style->setBg($colour, $fallback);
425
426
        return $this;
427
    }
428
429
    public function setForegroundColour(string $colour, string $fallback = null) : self
430
    {
431
        $this->style->setFg($colour, $fallback);
432
433
        return $this;
434
    }
435
436
    public function setWidth(int $width) : self
437
    {
438
        $this->style->setWidth($width);
439
440
        return $this;
441
    }
442
443
    public function setPadding(int $topBottom, int $leftRight = null) : self
444
    {
445
        $this->style->setPadding($topBottom, $leftRight);
446
447
        return $this;
448
    }
449
450
    public function setPaddingTopBottom(int $topBottom) : self
451
    {
452
        $this->style->setPaddingTopBottom($topBottom);
453
454
        return $this;
455
    }
456
457
    public function setPaddingLeftRight(int $leftRight) : self
458
    {
459
        $this->style->setPaddingLeftRight($leftRight);
460
461
        return $this;
462
    }
463
464
    public function setMarginAuto() : self
465
    {
466
        $this->style->setMarginAuto();
467
468
        return $this;
469
    }
470
471
    public function setMargin(int $margin) : self
472
    {
473
        $this->style->setMargin($margin);
474
475
        return $this;
476
    }
477
478
    public function setUnselectedMarker(string $marker) : self
479
    {
480
        $this->style->setUnselectedMarker($marker);
481
482
        return $this;
483
    }
484
485
    public function setSelectedMarker(string $marker) : self
486
    {
487
        $this->style->setSelectedMarker($marker);
488
489
        return $this;
490
    }
491
492
    public function setItemExtra(string $extra) : self
493
    {
494
        $this->style->setItemExtra($extra);
495
496
        //if we customise item extra, it means we most likely want to display it
497
        $this->displayExtra();
498
499
        return $this;
500
    }
501
502
    public function setTitleSeparator(string $separator) : self
503
    {
504
        $this->style->setTitleSeparator($separator);
505
506
        return $this;
507
    }
508
509
    public function setBorder(int $top, $right = null, $bottom = null, $left = null, string $colour = null) : self
510
    {
511
        $this->style->setBorder($top, $right, $bottom, $left, $colour);
512
513
        return $this;
514
    }
515
516
    public function setBorderTopWidth(int $width) : self
517
    {
518
        $this->style->setBorderTopWidth($width);
519
        
520
        return $this;
521
    }
522
523
    public function setBorderRightWidth(int $width) : self
524
    {
525
        $this->style->setBorderRightWidth($width);
526
527
        return $this;
528
    }
529
530
    public function setBorderBottomWidth(int $width) : self
531
    {
532
        $this->style->setBorderBottomWidth($width);
533
534
        return $this;
535
    }
536
537
    public function setBorderLeftWidth(int $width) : self
538
    {
539
        $this->style->setBorderLeftWidth($width);
540
541
        return $this;
542
    }
543
544
    public function setBorderColour(string $colour, $fallback = null) : self
545
    {
546
        $this->style->setBorderColour($colour, $fallback);
547
548
        return $this;
549
    }
550
551
    public function getStyle() : MenuStyle
552
    {
553
        return $this->style;
554
    }
555
556
    public function getTerminal() : Terminal
557
    {
558
        return $this->terminal;
559
    }
560
561
    private function getDefaultItems() : array
562
    {
563
        $actions = [];
564
        if ($this->subMenu) {
565
            $actions[] = (new SelectableItem($this->goBackButtonText, new GoBackAction))
566
                ->setStyle($this->menu->getSelectableStyle());
567
        }
568
569
        $actions[] = (new SelectableItem($this->exitButtonText, new ExitAction))
570
                ->setStyle($this->menu->getSelectableStyle());
571
572
        return $actions;
573
    }
574
575
    public function disableDefaultItems() : self
576
    {
577
        $this->disableDefaultItems = true;
578
579
        return $this;
580
    }
581
582
    public function displayExtra() : self
583
    {
584
        $this->style->setDisplaysExtra(true);
585
586
        return $this;
587
    }
588
589
    private function itemsHaveExtra(array $items) : bool
590
    {
591
        return !empty(array_filter($items, function (MenuItemInterface $item) {
592
            return $item->showsItemExtra();
593
        }));
594
    }
595
    
596
    public function build() : CliMenu
597
    {
598
        if (!$this->disableDefaultItems) {
599
            $this->menu->addItems($this->getDefaultItems());
600
        }
601
602
        if (!$this->style->getDisplaysExtra()) {
603
            $this->style->setDisplaysExtra($this->itemsHaveExtra($this->menu->getItems()));
604
        }
605
606
        return $this->menu;
607
    }
608
609
    public function setCheckableStyle(callable $itemCallable) : self
610
    {
611
        $this->menu->setCheckableStyle($itemCallable);
612
613
        return $this;
614
    }
615
616
    public function setRadioStyle(callable $itemCallable) : self
617
    {
618
        $this->menu->setRadioStyle($itemCallable);
619
620
        return $this;
621
    }
622
623
    public function setSelectableStyle(callable $itemCallable) : self
624
    {
625
        $this->menu->setSelectableStyle($itemCallable);
626
627
        return $this;
628
    }
629
}
630