Passed
Pull Request — master (#99)
by Sergei
02:55
created

WidgetFactory   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Test Coverage

Coverage 96.92%

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 55
c 5
b 0
f 1
dl 0
loc 148
ccs 63
cts 65
cp 0.9692
rs 10
wmc 20

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A initialize() 0 18 2
A setDefaultTheme() 0 3 1
A createWidget() 0 18 5
A assertWidgetDefaultThemesStructure() 0 14 4
B assertThemesStructure() 0 34 7
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;
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 ?Factory $factory = null;
25
26
    /**
27
     * @psalm-var array<string, array<string, array>>
28
     */
29
    private static array $themes = [];
30
31
    private static ?string $defaultTheme = null;
32
33
    /**
34
     * @psalm-var array<string, string>
35
     */
36
    private static array $widgetDefaultThemes = [];
37
38
    private function __construct()
39
    {
40
    }
41
42
    /**
43
     * @psalm-param array<string, mixed> $definitions
44
     * @psalm-param array<string, array<string, array>> $themes
45
     * @psalm-param array<string, string> $widgetDefaultThemes
46
     *
47
     * @throws InvalidConfigException
48
     *
49
     * @see Factory::__construct()
50
     */
51 40
    public static function initialize(
52
        ?ContainerInterface $container = null,
53
        array $definitions = [],
54
        bool $validate = true,
55
        array $themes = [],
56
        ?string $defaultTheme = null,
57
        array $widgetDefaultThemes = [],
58
    ): void {
59 40
        self::$factory = new Factory($container, $definitions, $validate);
60
61 40
        if ($validate) {
62 40
            self::assertThemesStructure($themes);
63 35
            self::assertWidgetDefaultThemesStructure($widgetDefaultThemes);
64
        }
65
66 33
        self::$themes = $themes;
67 33
        self::$defaultTheme = $defaultTheme;
68 33
        self::$widgetDefaultThemes = $widgetDefaultThemes;
69
    }
70
71 1
    public static function setDefaultTheme(?string $theme): void
72
    {
73 1
        self::$defaultTheme = $theme;
74
    }
75
76
    /**
77
     * Creates a widget defined by config passed.
78
     *
79
     * @param array $config The parameters for creating a widget.
80
     * @param string|null $theme The widget theme.
81
     *
82
     * @throws CircularReferenceException
83
     * @throws InvalidConfigException
84
     * @throws NotFoundException
85
     * @throws NotInstantiableException
86
     *
87
     * @see Factory::create()
88
     *
89
     * @psalm-suppress MixedInferredReturnType
90
     * @psalm-suppress MixedReturnStatement
91
     */
92 31
    public static function createWidget(array $config, ?string $theme = null): Widget
93
    {
94 31
        if (self::$factory === null) {
95 1
            self::$factory = new Factory();
96
        }
97
98 31
        $className = $config[ArrayDefinition::CLASS_NAME] ?? null;
99 31
        if (is_string($className)) {
100 31
            $theme ??= self::$widgetDefaultThemes[$className] ?? self::$defaultTheme;
101 31
            if ($theme !== null && isset(self::$themes[$theme][$className])) {
102 9
                $config = ArrayDefinitionHelper::merge(
103 9
                    self::$themes[$theme][$className],
104 9
                    $config
105 9
                );
106
            }
107
        }
108
109 31
        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

109
        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...
110
    }
111
112
    /**
113
     * @throws InvalidConfigException
114
     */
115 40
    private static function assertThemesStructure(array $themes): void
116
    {
117 40
        foreach ($themes as $theme => $definitions) {
118 18
            if (!is_string($theme)) {
119 1
                throw new InvalidConfigException(
120 1
                    sprintf('Theme name must be a string. Integer value "%s" given.', $theme)
121 1
                );
122
            }
123 17
            if (!is_array($definitions)) {
124 1
                throw new InvalidConfigException(
125 1
                    sprintf(
126 1
                        'Theme configuration must be an array. "%s" given for theme "%s".',
127 1
                        get_debug_type($definitions),
128 1
                        $theme,
129 1
                    )
130 1
                );
131
            }
132 16
            foreach ($definitions as $id => $definition) {
133 16
                if (!is_string($id)) {
134 1
                    throw new InvalidConfigException(
135 1
                        sprintf('Widget name must be a string. Integer value "%s" given in theme "%s".', $id, $theme)
136 1
                    );
137
                }
138 15
                if (!is_array($definition)) {
139 1
                    throw new InvalidConfigException(
140 1
                        sprintf(
141 1
                            'Widget themes supports array definitions only. "%s" given for "%s" definition in "%s" theme.',
142 1
                            get_debug_type($definition),
143 1
                            $id,
144 1
                            $theme,
145 1
                        )
146 1
                    );
147
                }
148 14
                DefinitionValidator::validateArrayDefinition($definition, $id);
149
            }
150
        }
151
    }
152
153
    /**
154
     * @throws InvalidConfigException
155
     */
156 35
    private static function assertWidgetDefaultThemesStructure(array $value): void
157
    {
158 35
        foreach ($value as $widget => $theme) {
159 5
            if (!is_string($widget)) {
160 1
                throw new InvalidConfigException(
161 1
                    sprintf('Widget class must be a string. Integer value "%s" given.', $widget)
162 1
                );
163
            }
164 4
            if (!is_string($theme)) {
165 1
                throw new InvalidConfigException(
166 1
                    sprintf(
167 1
                        'Theme name must be a string. "%s" given for widget "%s".',
168 1
                        get_debug_type($theme),
169 1
                        $widget,
170 1
                    )
171 1
                );
172
            }
173
        }
174
    }
175
}
176