Test Failed
Push — master ( 350674...c78563 )
by Andreas
22:08
created

get_component_icon()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 2
dl 0
loc 15
ccs 3
cts 3
cp 1
crap 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.helper
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * This class is a Factory that is responsible for loading and
11
 * establishing the interface to a MidCOM Component.
12
 *
13
 * <b>Working with components</b>
14
 *
15
 * Normally, two things are important when you deal with other components:
16
 *
17
 * First, if you want to list other components, or for example check whether they
18
 * are available, you should use the component manifest listing, known as $manifests.
19
 * It gives you all meta-information about the components.
20
 *
21
 * This should actually suffice for most normal operations.
22
 *
23
 * If you develop framework tools (like administration interfaces), you will also
24
 * need access to the component interface class, which can be obtained by
25
 * get_interface_class(). This class is derived from the component interface
26
 * baseclass and should give you everything you need to work with the component
27
 * and its information itself.
28
 *
29
 * Other than that, you should not have to deal with the components, perhaps with
30
 * the only exception of is_loaded() and load() to ensure other components are loaded
31
 * in case you need them and they are not a pure-code library.
32
 *
33
 * <b>Loading components</b>
34
 *
35
 * When the component loader receives a request it roughly works in
36
 * three stages:
37
 *
38
 * 1. Verify that the given component is valid in terms of the MidCOM Specification.
39
 * 2. Initialize the Component. Check whether all required concept classes exist.
40
 * 3. Return the various interface concepts upon each request
41
 *    from the framework.
42
 *
43
 * Stage 1 will do all basic sanity checking. If anything is missing, step 1
44
 * fails and the componentloader refuses to load the component.
45
 *
46
 * Stage 2 will then load the interfaces.php file from the midcom
47
 * directory. The existence of all required Interface classes is
48
 * then checked. If this check is successful, the concrete classes
49
 * of the various interface concepts are instantiated and stored
50
 * internally. The component is initialized by the call to
51
 * initialize() which should load everything necessary.
52
 *
53
 * Stage 3 is the final stage where the loader stays in memory in
54
 * order to return the loaded component's Interface instances upon request.
55
 *
56
 * In case you need an instance of the component loader to verify or
57
 * transform component paths, use midcom::get()->componentloader
58
 *
59
 * @package midcom.helper
60
 */
61
class midcom_helper__componentloader
62
{
63
    /**
64
     * This array contains a list of components that were tried to be loaded.
65
     * The components are added to this list *even* if the system only tried
66
     * to load it and failed. This way we protect against duplicate class errors
67
     * and the like if a defective class is tried to be loaded twice.
68
     *
69
     * The array maps component names to loading results. The loading result is
70
     * either false or true as per the result of the load call.
71
     *
72
     * @var array
73
     */
74
    private $_tried_to_load = [];
75
76
    /**
77
     * This stores the interface instances of the different loaded components,
78
     * indexed by their MidCOM Path.
79
     *
80
     * @var midcom_baseclasses_components_interface[]
81
     */
82
    private $_interface_classes = [];
83
84
    /**
85
     * @var midcom_core_manifest[]
86
     */
87
    private $manifests = [];
88
89
    private $components;
90
91
    public function __construct(array $components)
92
    {
93
        $this->components = $components;
94
    }
95
96
    /**
97
     * Invoke _load directly. If the loading process is unsuccessful, throw midcom_error.
98
     *
99
     * @param string $path    The component to load explicitly.
100 21
     */
101
    public function load($path)
102 21
    {
103 11
        if (!$this->_load($path)) {
104
            throw new midcom_error("Failed to load the component {$path}, see the debug log for more information");
105 10
        }
106
    }
107
108
    /**
109
     * Invoke _load directly. If the loading process is unsuccessful, false is returned.
110
     *
111
     * @param string $path    The component to load explicitly.
112
     * @return boolean Indicating success.
113 16
     */
114
    public function load_graceful($path) : bool
115 16
    {
116
        return $this->_load($path);
117
    }
118
119
    /**
120
     * Load the component specified by $path. If the component could not be loaded
121
     * successfully due to integrity errors, it will return false.
122
     *
123
     * @param string $path    The component to load.
124
     * @return boolean Indicating success.
125
     */
126
    private function _load($path) : bool
127
    {
128
        if (empty($path)) {
129
            debug_add("No component path given, aborting");
130
            return false;
131
        }
132
133
        // Check if this component is already loaded...
134
        if (array_key_exists($path, $this->_tried_to_load)) {
135
            debug_add("Component {$path} already loaded.");
136
            return $this->_tried_to_load[$path];
137
        }
138
139
        // Flag this path as loaded/failed, we'll set this flag to true when we reach
140
        // the end of this call.
141
        $this->_tried_to_load[$path] = false;
142
143
        // Check if the component is listed in the class manifest list. If not,
144
        // we immediately bail - anything went wrong while loading the component
145
        // (f.x. broken DBA classes).
146
        if (!array_key_exists($path, $this->components)) {
147
            debug_add("The component {$path} was not found in the manifest list. Cannot load it.",
148
                MIDCOM_LOG_WARN);
149
            return false;
150
        }
151
152
        $classname = midcom_baseclasses_components_interface::get_classname($path);
153
        $this->_interface_classes[$path] = new $classname;
154
        $this->_interface_classes[$path]->initialize($path);
155
        $this->_tried_to_load[$path] = true;
156
157
        return true;
158 36
    }
159
160 36
    /**
161 10
     * Returns true if the component identified by the MidCOM path $url
162 10
     * is already loaded and available for usage.
163
     *
164
     * @param string $path    The component to be queried.
165
     * @return boolean            true if it is loaded, false otherwise.
166 27
     */
167 14
    public function is_loaded($path) : bool
168 14
    {
169
        if ($path == 'midcom') {
170
            // MidCOM is "always loaded"
171
            return true;
172
        }
173 13
        return array_key_exists($path, $this->_interface_classes);
174
    }
175
176
    /**
177
     * Returns true if the component identified by the MidCOM path $url
178 13
     * is installed.
179 2
     *
180 2
     * @param string $path    The component to be queried.
181 2
     * @return boolean            true if it is loaded, false otherwise.
182
     */
183
    public function is_installed($path) : bool
184 12
    {
185 12
        return array_key_exists($path, $this->components);
186 12
    }
187 12
188
    /**
189 12
     * Returns an instance of the specified component's
190
     * interface class. The component is given in $path as a MidCOM path.
191
     * Such an instance will be cached by the framework so that only
192
     * one instance is always active for each component. Missing
193
     * components will be dynamically loaded into memory.
194
     *
195
     * @param string $path    The component name.
196
     */
197
    public function get_interface_class($path) : midcom_baseclasses_components_interface
198
    {
199 452
        if (!$this->is_loaded($path)) {
200
            $this->load($path);
201 452
        }
202
203 1
        return $this->_interface_classes[$path];
204
    }
205 452
206
    /**
207
     * Convert a component path (net.nehmer.blog) to a snippetpath (/net/nehmer/blog).
208
     *
209
     * @param string $component_name    Input string.
210
     * @return string        Converted string.
211
     */
212
    public function path_to_snippetpath($component_name)
213
    {
214
        if (array_key_exists($component_name, $this->components)) {
215 166
            return dirname($this->components[$component_name], 2);
216
        }
217 166
        debug_add("Component {$component_name} is not registered", MIDCOM_LOG_CRIT);
218
        return false;
219
    }
220 166
221
    /**
222
     * Convert a component path (net.nehmer.blog) to a class prefix (net_nehmer_blog).
223 1
     *
224
     * @param string $path    Input string.
225 1
     */
226 1
    public function path_to_prefix($path) : string
227
    {
228
        return strtr($path, ".", "_");
229 1
    }
230
231
    public function get_manifest(string $name) : ?midcom_core_manifest
232 1
    {
233 1
        if (!$this->is_installed($name)) {
234
            return null;
235
        }
236
        if (!array_key_exists($name, $this->manifests)) {
237
            $this->manifests[$name] = new midcom_core_manifest($this->components[$name]);
238
        }
239
        return $this->manifests[$name];
240
    }
241
242
    /**
243
     * This lists all available components in the systems in the form of their manifests,
244 450
     * indexed by the component name. Whenever possible you should refer to this listing
245
     * to gain information about the components available.
246 450
     *
247 17
     * @return midcom_core_manifest[]
248
     */
249
    public function get_manifests() : array
250 450
    {
251
        foreach (array_keys($this->components) as $name) {
252
            $this->get_manifest($name);
253
        }
254
        return $this->manifests;
255
    }
256
257
    /**
258
     * Build a complete set of custom data associated with a given component
259 511
     * identifier.
260
     *
261 511
     * @param string $component The custom data component index to look for.
262 511
     */
263
    public function get_all_manifest_customdata($component) : array
264 1
    {
265 1
        $result = [];
266
        foreach ($this->get_manifests() as $manifest) {
267
            if (array_key_exists($component, $manifest->customdata)) {
268
                $result[$manifest->name] = $manifest->customdata[$component];
269
            }
270
        }
271
        return $result;
272
    }
273 351
274
    public function get_component_icon($component, $provide_fallback = true)
275 351
    {
276
        if (!$this->is_installed($component)) {
277
            return null;
278
        }
279
280
        if (!empty($this->get_manifest($component)->icon)) {
281
            return $this->get_manifest($component)->icon;
282 1
        }
283
284 1
        if (!$provide_fallback) {
285
            return null;
286
        }
287
288
        return 'puzzle-piece';
289
    }
290
}
291