Passed
Push — master ( 82973f...cdeab4 )
by Alexander
01:27
created

Widget::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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
     */
41 2
    final public static function begin($config = []): self
42
    {
43 2
        if (\is_array($config) && !array_key_exists('__class', $config)) {
44 2
            $config['__class'] = static::class;
45
        }
46
47 2
        $widget = WidgetFactory::createWidget($config);
48
49 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...
50
51 2
        return $widget;
52
    }
53
54
    /**
55
     * Checks that the widget was opened with {@see begin()}. If so, runs it and returns content generated.
56
     *
57
     * @throws InvalidConfigException
58
     * @throws \InvalidConfigException
59
     */
60 3
    final public static function end(): string
61
    {
62 3
        if (empty(self::$stack)) {
63 1
            throw new InvalidConfigException(
64 1
                'Unexpected ' . static::class . '::end() call. A matching begin() is not found.'
65
            );
66
        }
67
68
        /** @var static $widget */
69 2
        $widget = array_pop(self::$stack);
70
71 2
        if (get_class($widget) !== static::class) {
72 1
            throw new InvalidConfigException('Expecting end() of ' . get_class($widget) . ', found ' . static::class);
73
        }
74
75 1
        return $widget->render();
76
    }
77
78
    /**
79
     * Creates a widget instance.
80
     *
81
     * @param string|array|callable $config parameters for creating a widget
82
     * @return Widget $widget.
83
     * @throws \Yiisoft\Factory\Exceptions\InvalidConfigException
84
     */
85 3
    final public static function widget($config = []): self
86
    {
87 3
        if (\is_array($config) && !array_key_exists('__class', $config)) {
88 3
            $config['__class'] = static::class;
89
        }
90
91 3
        return WidgetFactory::createWidget($config);
92
    }
93
94
    /**
95
     * Executes the widget.
96
     *
97
     * @return string the result of widget execution to be outputted.
98
     */
99 3
    public function render(): string
100
    {
101 3
        if (!$this->beforeRun()) {
102
            return '';
103
        }
104
105 3
        $result = $this->run();
106 3
        return $this->afterRun($result);
107
    }
108
109
    /**
110
     * This method is invoked right before the widget is executed.
111
     *
112
     * The return value of the method will determine whether the
113
     * widget should continue to run.
114
     *
115
     * When overriding this method, make sure you call the parent implementation like the following:
116
     *
117
     * ```php
118
     * public function beforeRun()
119
     * {
120
     *     if (!parent::beforeRun()) {
121
     *         return false;
122
     *     }
123
     *
124
     *     // your custom code here
125
     *
126
     *     return true; // or false to not run the widget
127
     * }
128
     * ```
129
     *
130
     * @return bool whether the widget should continue to be executed.
131
     */
132 3
    protected function beforeRun(): bool
133
    {
134 3
        return true;
135
    }
136
137
    /**
138
     * This method is invoked right after a widget is executed.
139
     *
140
     * The return value of the method will be used as the widget
141
     * return value.
142
     *
143
     * If you override this method, your code should look like the following:
144
     *
145
     * ```php
146
     * public function afterRun($result)
147
     * {
148
     *     $result = parent::afterRun($result);
149
     *     // your custom code here
150
     *     return $result;
151
     * }
152
     * ```
153
     *
154
     * @param string $result the widget return result.
155
     *
156
     * @return string the processed widget result.
157
     */
158 3
    protected function afterRun(string $result): string
159
    {
160 3
        return $result;
161
    }
162
163
    /**
164
     * Allows not to call `->render()` explicitly:
165
     *
166
     * ```php
167
     * <?= MyWidget::widget()->name('test') ?>
168
     * ```
169
     */
170
    public function __toString(): string
171
    {
172
        return $this->render();
173
    }
174
}
175