Passed
Push — master ( 125f6d...0fede7 )
by Alexander
02:05
created

Widget   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Test Coverage

Coverage 96.15%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 21
c 3
b 0
f 0
dl 0
loc 151
ccs 25
cts 26
cp 0.9615
rs 10
wmc 12

7 Methods

Rating   Name   Duplication   Size   Complexity  
A end() 0 16 3
A widget() 0 7 3
A __toString() 0 3 1
A begin() 0 4 1
A beforeRun() 0 3 1
A afterRun() 0 3 1
A render() 0 8 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Widget;
6
7
use RuntimeException;
8
9
/**
10
 * Widget generates a string content based on some logic and input data.
11
 * These are typically used in templates to conceal complex HTML rendering logic.
12
 *
13
 * This is the base class that is meant to be inherited when implementing your own widgets.
14
 */
15
abstract class Widget
16
{
17
    /**
18
     * The widgets that are currently opened and not yet closed.
19
     * This property is maintained by {@see begin()} and {@see end()} methods.
20
     *
21
     * @var array
22
     */
23
    private static array $stack;
24
25
    /**
26
     * Used to open a wrapping widget (the one with begin/end).
27
     *
28
     * When implementing this method, don't forget to call parent::begin().
29
     *
30
     * @return string|null Opening part of widget markup.
31
     */
32 4
    public function begin(): ?string
33
    {
34 4
        self::$stack[] = $this;
35 4
        return null;
36
    }
37
38
    /**
39
     * Renders widget content.
40
     *
41
     * This method is used by {@see render()} and is meant to be overridden
42
     * when implementing concrete widget.
43
     */
44
    abstract protected function run(): string;
45
46
    /**
47
     * Checks that the widget was opened with {@see begin()}. If so, runs it and returns content generated.
48
     *
49
     * @throws RuntimeException
50
     */
51 7
    final public static function end(): string
52
    {
53 7
        if (empty(self::$stack)) {
54 3
            throw new RuntimeException(
55 3
                'Unexpected ' . static::class . '::end() call. A matching begin() is not found.'
56
            );
57
        }
58
59
        /** @var static $widget */
60 4
        $widget = array_pop(self::$stack);
61
62 4
        if (get_class($widget) !== static::class) {
63 1
            throw new RuntimeException('Expecting end() of ' . get_class($widget) . ', found ' . static::class . '.');
64
        }
65
66 3
        return $widget->render();
67
    }
68
69
    /**
70
     * Creates a widget instance.
71
     *
72
     * @param array|callable|string $config parameters for creating a widget
73
     *
74
     * @throws \Yiisoft\Factory\Exceptions\InvalidConfigException
75
     *
76
     * @return static widget instance
77
     */
78 13
    final public static function widget($config = []): self
79
    {
80 13
        if (\is_array($config) && !array_key_exists('__class', $config)) {
81 13
            $config['__class'] = static::class;
82
        }
83
84 13
        return WidgetFactory::createWidget($config);
85
    }
86
87
    /**
88
     * Executes the widget.
89
     *
90
     * @return string the result of widget execution to be outputted.
91
     */
92 7
    final public function render(): string
93
    {
94 7
        if (!$this->beforeRun()) {
95
            return '';
96
        }
97
98 7
        $result = $this->run();
99 7
        return $this->afterRun($result);
100
    }
101
102
    /**
103
     * This method is invoked right before the widget is executed.
104
     *
105
     * The return value of the method will determine whether the
106
     * widget should continue to run.
107
     *
108
     * When overriding this method, make sure you call the parent implementation like the following:
109
     *
110
     * ```php
111
     * public function beforeRun()
112
     * {
113
     *     if (!parent::beforeRun()) {
114
     *         return false;
115
     *     }
116
     *
117
     *     // your custom code here
118
     *
119
     *     return true; // or false to not run the widget
120
     * }
121
     * ```
122
     *
123
     * @return bool whether the widget should continue to be executed.
124
     */
125 7
    protected function beforeRun(): bool
126
    {
127 7
        return true;
128
    }
129
130
    /**
131
     * This method is invoked right after a widget is executed.
132
     *
133
     * The return value of the method will be used as the widget
134
     * return value.
135
     *
136
     * If you override this method, your code should look like the following:
137
     *
138
     * ```php
139
     * public function afterRun($result)
140
     * {
141
     *     $result = parent::afterRun($result);
142
     *     // your custom code here
143
     *     return $result;
144
     * }
145
     * ```
146
     *
147
     * @param string $result the widget return result.
148
     *
149
     * @return string the processed widget result.
150
     */
151 7
    protected function afterRun(string $result): string
152
    {
153 7
        return $result;
154
    }
155
156
    /**
157
     * Allows not to call `->render()` explicitly:
158
     *
159
     * ```php
160
     * <?= MyWidget::widget()->name('test') ?>
161
     * ```
162
     */
163 1
    final public function __toString(): string
164
    {
165 1
        return $this->render();
166
    }
167
}
168