Passed
Push — master ( 0074b5...431277 )
by Alexander
02:14
created

Widget::afterRun()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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