Passed
Push — master ( c1edad...c0372e )
by Sergei
02:38
created

WidgetFactory   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 60
c 5
b 0
f 1
dl 0
loc 162
ccs 66
cts 66
cp 1
rs 10
wmc 21

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A assertWidgetDefaultThemesStructure() 0 14 4
B assertThemesStructure() 0 34 7
A initialize() 0 20 2
A setDefaultTheme() 0 3 1
A createWidget() 0 25 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Widget;
6
7
use Psr\Container\ContainerInterface;
8
use Yiisoft\Definitions\ArrayDefinition;
9
use Yiisoft\Definitions\Exception\CircularReferenceException;
10
use Yiisoft\Definitions\Exception\InvalidConfigException;
11
use Yiisoft\Definitions\Exception\NotInstantiableException as FactoryNotInstantiableException;
12
use Yiisoft\Definitions\Helpers\ArrayDefinitionHelper;
13
use Yiisoft\Definitions\Helpers\DefinitionValidator;
14
use Yiisoft\Factory\NotFoundException;
15
use Yiisoft\Factory\Factory;
16
17
/**
18
 * WidgetFactory creates an instance of the widget based on the specified configuration
19
 * {@see WidgetFactory::createWidget()}. Before creating a widget, you need to initialize
20
 * the WidgetFactory with {@see WidgetFactory::initialize()}.
21
 */
22
final class WidgetFactory
23
{
24
    private static bool $initialized = false;
25
    private static ?Factory $factory = null;
26
27
    /**
28
     * @psalm-var array<string, array<string, array>>
29
     */
30
    private static array $themes = [];
31
32
    private static ?string $defaultTheme = null;
33
34
    /**
35
     * @psalm-var array<string, string>
36
     */
37
    private static array $widgetDefaultThemes = [];
38
39
    /**
40
     * @codeCoverageIgnore
41
     */
42
    private function __construct()
43
    {
44
    }
45
46
    /**
47
     * @psalm-param array<string, mixed> $definitions
48
     * @psalm-param array<string, array<string, array>> $themes
49
     * @psalm-param array<string, string> $widgetDefaultThemes
50
     *
51
     * @throws InvalidConfigException
52
     *
53
     * @see Factory::__construct()
54
     */
55 42
    public static function initialize(
56
        ?ContainerInterface $container = null,
57
        array $definitions = [],
58
        bool $validate = true,
59
        array $themes = [],
60
        ?string $defaultTheme = null,
61
        array $widgetDefaultThemes = [],
62
    ): void {
63 42
        self::$factory = new Factory($container, $definitions, $validate);
64
65 42
        if ($validate) {
66 42
            self::assertThemesStructure($themes);
67 37
            self::assertWidgetDefaultThemesStructure($widgetDefaultThemes);
68
        }
69
70 35
        self::$themes = $themes;
71 35
        self::$defaultTheme = $defaultTheme;
72 35
        self::$widgetDefaultThemes = $widgetDefaultThemes;
73
74 35
        self::$initialized = true;
75
    }
76
77 1
    public static function setDefaultTheme(?string $theme): void
78
    {
79 1
        self::$defaultTheme = $theme;
80
    }
81
82
    /**
83
     * Creates a widget defined by config passed.
84
     *
85
     * @param array $config The parameters for creating a widget.
86
     * @param string|null $theme The widget theme.
87
     *
88
     * @throws CircularReferenceException
89
     * @throws InvalidConfigException
90
     * @throws NotFoundException
91
     * @throws FactoryNotInstantiableException
92
     *
93
     * @see Factory::create()
94
     *
95
     * @psalm-suppress MixedInferredReturnType
96
     * @psalm-suppress MixedReturnStatement
97
     */
98 33
    public static function createWidget(array $config, ?string $theme = null): Widget
99
    {
100 33
        if (self::$factory === null) {
101 2
            self::$factory = new Factory();
102
        }
103
104 33
        $className = $config[ArrayDefinition::CLASS_NAME] ?? null;
105 33
        if (is_string($className)) {
106 33
            $theme ??= self::$widgetDefaultThemes[$className] ?? self::$defaultTheme;
107 33
            if ($theme !== null && isset(self::$themes[$theme][$className])) {
108 9
                $config = ArrayDefinitionHelper::merge(
109 9
                    self::$themes[$theme][$className],
110 9
                    $config
111 9
                );
112
            }
113
        }
114
115
        try {
116 33
            return self::$factory->create($config);
0 ignored issues
show
Bug introduced by
The method create() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

116
            return self::$factory->/** @scrutinizer ignore-call */ create($config);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
117 2
        } catch (FactoryNotInstantiableException $exception) {
118
            /**
119
             * @var string $className When `$className` is not string, `$factory->create()` does not throw
120
             * {@see FactoryNotInstantiableException} exception.
121
             */
122 2
            throw new NotInstantiableException($className, self::$initialized, $exception);
123
        }
124
    }
125
126
    /**
127
     * @throws InvalidConfigException
128
     */
129 42
    private static function assertThemesStructure(array $themes): void
130
    {
131 42
        foreach ($themes as $theme => $definitions) {
132 18
            if (!is_string($theme)) {
133 1
                throw new InvalidConfigException(
134 1
                    sprintf('Theme name must be a string. Integer value "%s" given.', $theme)
135 1
                );
136
            }
137 17
            if (!is_array($definitions)) {
138 1
                throw new InvalidConfigException(
139 1
                    sprintf(
140 1
                        'Theme configuration must be an array. "%s" given for theme "%s".',
141 1
                        get_debug_type($definitions),
142 1
                        $theme,
143 1
                    )
144 1
                );
145
            }
146 16
            foreach ($definitions as $id => $definition) {
147 16
                if (!is_string($id)) {
148 1
                    throw new InvalidConfigException(
149 1
                        sprintf('Widget name must be a string. Integer value "%s" given in theme "%s".', $id, $theme)
150 1
                    );
151
                }
152 15
                if (!is_array($definition)) {
153 1
                    throw new InvalidConfigException(
154 1
                        sprintf(
155 1
                            'Widget themes supports array definitions only. "%s" given for "%s" definition in "%s" theme.',
156 1
                            get_debug_type($definition),
157 1
                            $id,
158 1
                            $theme,
159 1
                        )
160 1
                    );
161
                }
162 14
                DefinitionValidator::validateArrayDefinition($definition, $id);
163
            }
164
        }
165
    }
166
167
    /**
168
     * @throws InvalidConfigException
169
     */
170 37
    private static function assertWidgetDefaultThemesStructure(array $value): void
171
    {
172 37
        foreach ($value as $widget => $theme) {
173 5
            if (!is_string($widget)) {
174 1
                throw new InvalidConfigException(
175 1
                    sprintf('Widget class must be a string. Integer value "%s" given.', $widget)
176 1
                );
177
            }
178 4
            if (!is_string($theme)) {
179 1
                throw new InvalidConfigException(
180 1
                    sprintf(
181 1
                        'Theme name must be a string. "%s" given for widget "%s".',
182 1
                        get_debug_type($theme),
183 1
                        $widget,
184 1
                    )
185 1
                );
186
            }
187
        }
188
    }
189
}
190