Widget   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 24
c 7
b 0
f 0
dl 0
loc 119
ccs 31
cts 31
cp 1
rs 10
wmc 8

5 Methods

Rating   Name   Duplication   Size   Complexity  
A end() 0 22 3
A begin() 0 4 1
A __toString() 0 3 1
A getThemeConfig() 0 3 1
A widget() 0 14 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Widget;
6
7
use RuntimeException;
8
use Yiisoft\Definitions\ArrayDefinition;
9
use Yiisoft\Definitions\Exception\CircularReferenceException;
10
use Yiisoft\Definitions\Exception\InvalidConfigException;
11
use Yiisoft\Definitions\Exception\NotInstantiableException;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Yiisoft\Widget\NotInstantiableException. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
12
use Yiisoft\Definitions\Helpers\ArrayDefinitionHelper;
13
use Yiisoft\Factory\NotFoundException;
14
use Yiisoft\Html\NoEncodeStringableInterface;
15
16
use function array_pop;
17
use function sprintf;
18
19
/**
20
 * Widget generates a string content based on some logic and input data.
21
 * These are typically used in templates to conceal complex HTML rendering logic.
22
 *
23
 * This is the base class that is meant to be inherited when implementing your own widgets.
24
 */
25
abstract class Widget implements NoEncodeStringableInterface
26
{
27
    /**
28
     * The widgets that are currently opened and not yet closed.
29
     * This property is maintained by {@see begin()} and {@see end()} methods.
30
     *
31
     * @var static[]
32
     */
33
    private static array $stack = [];
34
35
    /**
36
     * Used to open a wrapping widget (the one with begin/end).
37
     *
38
     * When implementing this method, don't forget to call `parent::begin()`.
39
     *
40
     * @return string|null Opening part of widget markup.
41
     */
42 4
    public function begin(): ?string
43
    {
44 4
        self::$stack[] = $this;
45 4
        return null;
46
    }
47
48
    /**
49
     * Checks that the widget was opened with {@see begin()}. If so, runs it and returns content generated.
50
     *
51
     * @throws RuntimeException
52
     */
53 7
    final public static function end(): string
54
    {
55 7
        if (self::$stack === []) {
56 3
            throw new RuntimeException(sprintf(
57 3
                'Unexpected "%s::end()" call. A matching "%s::begin()" is not found.',
58 3
                static::class,
59 3
                static::class,
60 3
            ));
61
        }
62
63 4
        $widget = array_pop(self::$stack);
64 4
        $widgetClass = $widget::class;
65
66 4
        if ($widgetClass !== static::class) {
67 1
            throw new RuntimeException(sprintf(
68 1
                'Expecting "%s::end()" call, found "%s::end()".',
69 1
                $widgetClass,
70 1
                static::class,
71 1
            ));
72
        }
73
74 3
        return $widget->render();
75
    }
76
77
    /**
78
     * Creates a widget instance.
79
     *
80
     * @param array $constructorArguments The constructor arguments.
81
     * @param array $config The configuration for creating a widget. For a description of the configuration syntax, see
82
     * array definitions documentation in the Yii Definitions by link
83
     * {@link https://github.com/yiisoft/definitions#arraydefinition).
84
     * @param string|null $theme The widget theme.
85
     *
86
     * @throws InvalidConfigException
87
     * @throws CircularReferenceException
88
     * @throws NotInstantiableException
89
     * @throws NotFoundException
90
     *
91
     * @return static The widget instance.
92
     */
93 34
    final public static function widget(
94
        array $constructorArguments = [],
95
        array $config = [],
96
        ?string $theme = null
97
    ): static {
98 34
        $config = ArrayDefinitionHelper::merge(
99 34
            static::getThemeConfig($theme),
100 34
            $config,
101 34
            empty($constructorArguments) ? [] : [ArrayDefinition::CONSTRUCTOR => $constructorArguments],
102 34
        );
103
104 33
        $config[ArrayDefinition::CLASS_NAME] = static::class;
105
106 33
        return WidgetFactory::createWidget($config, $theme);
107
    }
108
109
    /**
110
     * Returns configuration that will be merged with configuration passed to {@see widget()} method.
111
     *
112
     * @param string|null $theme The widget theme.
113
     *
114
     * @return array Configuration in the form of array definition (see syntax description in the Yii Definitions
115
     * documentation by link {@link https://github.com/yiisoft/definitions#arraydefinition}).
116
     *
117
     * @infection-ignore-all
118
     */
119 33
    protected static function getThemeConfig(?string $theme): array
120
    {
121 33
        return [];
122
    }
123
124
    /**
125
     * Allows not to call `->render()` explicitly:
126
     *
127
     * ```php
128
     * <?= MyWidget::widget(); ?>
129
     * ```
130
     */
131 1
    final public function __toString(): string
132
    {
133 1
        return $this->render();
134
    }
135
136
    /**
137
     * Renders widget content.
138
     *
139
     * This method must be overridden when implementing concrete widget.
140
     *
141
     * @return string The result of widget execution to be outputted.
142
     */
143
    abstract public function render(): string;
144
}
145