Completed
Push — master ( 23e787...2652bc )
by Andreas
33:54 queued 13:51
created

midcom_helper__componentloader::get_manifest()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
crap 3.0416
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 7
    private function load(string $path)
84
    {
85 7
        if (empty($path)) {
86
            throw new midcom_error("No component path given, aborting");
87
        }
88
89
        // Check if the component is listed in the class manifest list. If not,
90
        // we immediately bail - anything went wrong while loading the component
91
        // (f.x. broken DBA classes).
92 7
        if (!array_key_exists($path, $this->components)) {
93
            throw new midcom_error("The component {$path} was not found in the manifest list. Cannot load it.");
94
        }
95
96 7
        $classname = midcom_baseclasses_components_interface::get_classname($path);
97 7
        $this->_interface_classes[$path] = new $classname;
98 7
        $this->_interface_classes[$path]->initialize($path);
99 7
    }
100
101
    /**
102
     * Returns true if the component identified by the MidCOM path $url
103
     * is installed.
104
     */
105 225
    public function is_installed(string $path) : bool
106
    {
107 225
        return array_key_exists($path, $this->components);
108
    }
109
110
    /**
111
     * Returns an instance of the specified component's
112
     * interface class. The component is given in $path as a MidCOM path.
113
     * Such an instance will be cached by the framework so that only
114
     * one instance is always active for each component. Missing
115
     * components will be dynamically loaded into memory.
116
     */
117 452
    public function get_interface_class(string $path) : midcom_baseclasses_components_interface
118
    {
119 452
        if (!array_key_exists($path, $this->_interface_classes)) {
120 7
            $this->load($path);
121
        }
122
123 452
        return $this->_interface_classes[$path];
124
    }
125
126
    /**
127
     * Convert a component path (net.nehmer.blog) to a snippetpath (/net/nehmer/blog).
128
     */
129 478
    public function path_to_snippetpath(string $component_name) : string
130
    {
131 478
        if (array_key_exists($component_name, $this->components)) {
132 478
            return dirname($this->components[$component_name], 2);
133
        }
134
        throw new midcom_error("Component {$component_name} is not registered");
135
    }
136
137
    /**
138
     * Convert a component path (net.nehmer.blog) to a class prefix (net_nehmer_blog).
139
     */
140 343
    public function path_to_prefix(string $path) : string
141
    {
142 343
        return strtr($path, ".", "_");
143
    }
144
145 31
    public function get_manifest(string $name) : ?midcom_core_manifest
146
    {
147 31
        if (!$this->is_installed($name)) {
148
            return null;
149
        }
150 31
        if (!array_key_exists($name, $this->manifests)) {
151 1
            $this->manifests[$name] = new midcom_core_manifest($this->components[$name]);
152
        }
153 31
        return $this->manifests[$name];
154
    }
155
156
    /**
157
     * This lists all available components in the systems in the form of their manifests,
158
     * indexed by the component name. Whenever possible you should refer to this listing
159
     * to gain information about the components available.
160
     *
161
     * @return midcom_core_manifest[]
162
     */
163 22
    public function get_manifests() : array
164
    {
165 22
        array_map([$this, 'get_manifest'], array_keys($this->components));
166 22
        return $this->manifests;
167
    }
168
169
    /**
170
     * Build a complete set of custom data associated with a given component
171
     * identifier.
172
     */
173 13
    public function get_all_manifest_customdata(string $component) : array
174
    {
175 13
        $result = [];
176 13
        foreach ($this->get_manifests() as $manifest) {
177 13
            if (array_key_exists($component, $manifest->customdata)) {
178 13
                $result[$manifest->name] = $manifest->customdata[$component];
179
            }
180
        }
181 13
        return $result;
182
    }
183
184 2
    public function get_component_icon(string $component, bool $provide_fallback = true) : ?string
185
    {
186 2
        if (!$this->is_installed($component)) {
187
            return null;
188
        }
189
190 2
        if (!empty($this->get_manifest($component)->icon)) {
191 2
            return $this->get_manifest($component)->icon;
192
        }
193
194 1
        if (!$provide_fallback) {
195
            return null;
196
        }
197
198 1
        return 'puzzle-piece';
199
    }
200
}
201