Passed
Push — master ( 67760a...0ff2cc )
by Andreas
17:17
created

midcom_helper__componentloader::load_graceful()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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
 * <b>Loading components</b>
30
 *
31
 * When the component loader receives a request it roughly works in
32
 * three stages:
33
 *
34
 * 1. Verify that the given component is valid in terms of the MidCOM Specification.
35
 * 2. Initialize the Component. Check whether all required concept classes exist.
36
 * 3. Return the various interface concepts upon each request
37
 *    from the framework.
38
 *
39
 * Stage 1 will do all basic sanity checking. If anything is missing, step 1
40
 * fails and the componentloader refuses to load the component.
41
 *
42
 * Stage 2 will then load the interfaces.php file from the midcom
43
 * directory. The existence of all required Interface classes is
44
 * then checked. If this check is successful, the concrete classes
45
 * of the various interface concepts are instantiated and stored
46
 * internally. The component is initialized by the call to
47
 * initialize() which should load everything necessary.
48
 *
49
 * Stage 3 is the final stage where the loader stays in memory in
50
 * order to return the loaded component's Interface instances upon request.
51
 *
52
 * In case you need an instance of the component loader to verify or
53
 * transform component paths, use midcom::get()->componentloader
54
 *
55
 * @package midcom.helper
56
 */
57
class midcom_helper__componentloader
58
{
59
    /**
60
     * This stores the interface instances of the different loaded components,
61
     * indexed by their MidCOM Path.
62
     *
63
     * @var midcom_baseclasses_components_interface[]
64
     */
65
    private $_interface_classes = [];
66
67
    /**
68
     * @var midcom_core_manifest[]
69
     */
70
    private $manifests = [];
71
72
    private $components;
73
74 4
    public function __construct(array $components)
75
    {
76 4
        $this->components = $components;
77 4
    }
78
79
    /**
80
     * Load the component specified by $path. If the component could not be loaded
81
     * successfully due to integrity errors, it will return false.
82
     *
83
     * @param string $path    The component to load.
84
     */
85 7
    private function load(string $path)
86
    {
87 7
        if (empty($path)) {
88
            throw new midcom_error("No component path given, aborting");
89
        }
90
91
        // Check if the component is listed in the class manifest list. If not,
92
        // we immediately bail - anything went wrong while loading the component
93
        // (f.x. broken DBA classes).
94 7
        if (!array_key_exists($path, $this->components)) {
95
            throw new midcom_error("The component {$path} was not found in the manifest list. Cannot load it.");
96
        }
97
98 7
        $classname = midcom_baseclasses_components_interface::get_classname($path);
99 7
        $this->_interface_classes[$path] = new $classname;
100 7
        $this->_interface_classes[$path]->initialize($path);
101 7
    }
102
103
    /**
104
     * Returns true if the component identified by the MidCOM path $url
105
     * is installed.
106
     *
107
     * @param string $path    The component to be queried.
108
     * @return boolean            true if it is loaded, false otherwise.
109
     */
110 518
    public function is_installed($path) : bool
111
    {
112 518
        return array_key_exists($path, $this->components);
113
    }
114
115
    /**
116
     * Returns an instance of the specified component's
117
     * interface class. The component is given in $path as a MidCOM path.
118
     * Such an instance will be cached by the framework so that only
119
     * one instance is always active for each component. Missing
120
     * components will be dynamically loaded into memory.
121
     *
122
     * @param string $path    The component name.
123
     */
124 450
    public function get_interface_class($path) : midcom_baseclasses_components_interface
125
    {
126 450
        if (!array_key_exists($path, $this->_interface_classes)) {
127 7
            $this->load($path);
128
        }
129
130 450
        return $this->_interface_classes[$path];
131
    }
132
133
    /**
134
     * Convert a component path (net.nehmer.blog) to a snippetpath (/net/nehmer/blog).
135
     *
136
     * @param string $component_name    Input string.
137
     * @return string        Converted string.
138
     */
139 511
    public function path_to_snippetpath($component_name)
140
    {
141 511
        if (array_key_exists($component_name, $this->components)) {
142 511
            return dirname($this->components[$component_name], 2);
143
        }
144 1
        debug_add("Component {$component_name} is not registered", MIDCOM_LOG_CRIT);
145 1
        return false;
146
    }
147
148
    /**
149
     * Convert a component path (net.nehmer.blog) to a class prefix (net_nehmer_blog).
150
     *
151
     * @param string $path    Input string.
152
     */
153 346
    public function path_to_prefix($path) : string
154
    {
155 346
        return strtr($path, ".", "_");
156
    }
157
158 513
    public function get_manifest(string $name) : ?midcom_core_manifest
159
    {
160 513
        if (!$this->is_installed($name)) {
161
            return null;
162
        }
163 513
        if (!array_key_exists($name, $this->manifests)) {
164 1
            $this->manifests[$name] = new midcom_core_manifest($this->components[$name]);
165
        }
166 513
        return $this->manifests[$name];
167
    }
168
169
    /**
170
     * This lists all available components in the systems in the form of their manifests,
171
     * indexed by the component name. Whenever possible you should refer to this listing
172
     * to gain information about the components available.
173
     *
174
     * @return midcom_core_manifest[]
175
     */
176 22
    public function get_manifests() : array
177
    {
178 22
        foreach (array_keys($this->components) as $name) {
179 22
            $this->get_manifest($name);
180
        }
181 22
        return $this->manifests;
182
    }
183
184
    /**
185
     * Build a complete set of custom data associated with a given component
186
     * identifier.
187
     *
188
     * @param string $component The custom data component index to look for.
189
     */
190 13
    public function get_all_manifest_customdata($component) : array
191
    {
192 13
        $result = [];
193 13
        foreach ($this->get_manifests() as $manifest) {
194 13
            if (array_key_exists($component, $manifest->customdata)) {
195 13
                $result[$manifest->name] = $manifest->customdata[$component];
196
            }
197
        }
198 13
        return $result;
199
    }
200
201 2
    public function get_component_icon($component, $provide_fallback = true)
202
    {
203 2
        if (!$this->is_installed($component)) {
204
            return null;
205
        }
206
207 2
        if (!empty($this->get_manifest($component)->icon)) {
208 2
            return $this->get_manifest($component)->icon;
209
        }
210
211 1
        if (!$provide_fallback) {
212
            return null;
213
        }
214
215 1
        return 'puzzle-piece';
216
    }
217
}
218