Passed
Push — master ( c8f0ab...3e084c )
by Alexander
07:00
created

Widget   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Test Coverage

Coverage 80.77%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 25
c 3
b 0
f 0
dl 0
loc 159
ccs 21
cts 26
cp 0.8077
rs 10
wmc 15

8 Methods

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