Completed
Pull Request — master (#593)
by Richard
21:18
created

Manager::locate()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5.7873

Importance

Changes 0
Metric Value
cc 5
eloc 20
nc 3
nop 1
dl 0
loc 32
ccs 13
cts 19
cp 0.6842
crap 5.7873
rs 9.2888
c 0
b 0
f 0
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
namespace Xoops\Core\Service;
13
14
use Xmf\Yaml;
15
16
/**
17
 * Xoops services manager, locate, register, choose and dispatch
18
 *
19
 * @category  Xoops\Core\Service\Manager
20
 * @package   Xoops\Core
21
 * @author    Richard Griffith <[email protected]>
22
 * @copyright 2013-2019 The XOOPS Project (https://xoops.org)
23
 * @license   GNU GPL 2 or later (https://www.gnu.org/licenses/gpl-2.0.html)
24
 */
25
class Manager
26
{
27
    /**
28
     * Service Mode constant - Exclusive mode where only one located service
29
     * will be used.
30
     */
31
    const MODE_EXCLUSIVE = 1;
32
33
    /**
34
     * Service Mode constant - Choice mode where one service from potentially
35
     * many located services will be used. The service dispatched will be selected
36
     * by system default.
37
     */
38
    const MODE_CHOICE = 2;
39
40
    /**
41
     * Service Mode constant - Choice mode where one service from potentially many
42
     * located services will be used. The service dispatched will be selected by user
43
     * preference, or system default if no user valid preference is available.
44
     */
45
    const MODE_PREFERENCE = 4;
46
47
    /**
48
     * Service Mode constant - Multiple mode where all located services will be
49
     * dispatched in priority order.
50
     */
51
    const MODE_MULTIPLE  = 8;
52
53
    /**
54
     * Provider priorities
55
     */
56
    const PRIORITY_SELECTED = 0;
57
    const PRIORITY_HIGH     = 1;
58
    const PRIORITY_MEDIUM   = 5;
59
    const PRIORITY_LOW      = 9;
60
61
    /**
62
     * Services registry - array keyed on service name, with provider object as value
63
     *
64
     * @var array
65
     */
66
    protected $services = [];
67
68
    /**
69
     * Provider Preferences - array keyed on service name, where each element is
70
     * an array of provider name => priority entries
71
     *
72
     * @var array|null
73
     */
74
    protected $providerPrefs = null;
75
76
    /**
77
     * @var string config file with provider preferences
78
     */
79
    private $providerPrefsFilename = 'var/configs/system_provider_preferences.yml';
80
81
    /**
82
     * @var string config cache key
83
     */
84
    private $providerPrefsCacheKey = 'system/provider/prefs';
85
86
    /**
87
     * __construct
88
     */
89 1
    protected function __construct()
90
    {
91 1
        $this->providerPrefs = $this->readProviderPrefs();
92 1
    }
93
94
    /**
95
     * Allow one instance only!
96
     *
97
     * @return Manager instance
98
     */
99 14
    public static function getInstance()
100
    {
101 14
        static $instance = false;
102
103 14
        if (!$instance) {
104 1
            $instance = new Manager();
105
        }
106
107 14
        return $instance;
108
    }
109
110
    /**
111
     * readYamlProviderPrefs - read configured provider preferences from file
112
     *
113
     * @return array of configured provider preferences
114
     */
115 1
    public function readYamlProviderPrefs()
116
    {
117 1
        $xoops = \Xoops::getInstance();
118
119 1
        $preferences = [];
120
121
        try {
122 1
            $file = $xoops->path($this->providerPrefsFilename);
123 1
            if (file_exists($file)) {
124
                $preferences = Yaml::read($file);
125
            }
126 1
            if (empty($preferences)) {
127 1
                $preferences = [];
128
            }
129
        } catch (\Exception $e) {
130
            $xoops->events()->triggerEvent('core.exception', $e);
131
            $preferences = [];
132
        }
133 1
        return $preferences;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $preferences also could return the type boolean which is incompatible with the documented return type array.
Loading history...
134
    }
135
136
    /**
137
     * readProviderPrefs - read configured provider preferences from cache
138
     *
139
     * @return array of configured provider preferences
140
     */
141 1
    protected function readProviderPrefs()
142
    {
143 1
        $xoops = \Xoops::getInstance();
144 1
        return $xoops->cache()->cacheRead(
145 1
            $this->providerPrefsCacheKey,
146 1
            [$this, 'readYamlProviderPrefs']
147
        );
148
    }
149
150
    /**
151
     * saveProviderPrefs - record array of provider preferences in config file, and
152
     * update cache
153
     *
154
     * @param array $providerPrefs array of provider preferences to save
155
     *
156
     * @return void
157
     */
158
    protected function saveProviderPrefs($providerPrefs)
159
    {
160
        if (is_array($providerPrefs)) {
0 ignored issues
show
introduced by
The condition is_array($providerPrefs) is always true.
Loading history...
161
            $xoops = \Xoops::getInstance();
162
            try {
163
                Yaml::save($providerPrefs, $xoops->path($this->providerPrefsFilename));
164
                $xoops->cache()->write($this->providerPrefsCacheKey, $providerPrefs);
165
            } catch (\Exception $e) {
166
                $xoops->events()->triggerEvent('core.exception', $e);
167
            }
168
        }
169
    }
170
171
    /**
172
     * saveChoice - record priority choices for service providers
173
     *
174
     * This registers a permanent choice (i.e. setting system default) that will
175
     * persist after the lifetime of this service manager.
176
     *
177
     * @param string $service the service name being set
178
     * @param array  $choices array of priorities for each of the named service providers
179
     *
180
     * @return void
181
     */
182
    public function saveChoice($service, $choices)
183
    {
184
        // read current preferences
185
        $preferences = $this->readProviderPrefs();
186
        // replace preferences for selected service
187
        $preferences[$service] = $choices;
188
        // save the changes
189
        $this->saveProviderPrefs($preferences);
190
        // apply to current manager instance
191
        $this->registerChoice($service, $choices);
192
    }
193
194
    /**
195
     * registerChoice - record priority choices for service providers
196
     *
197
     * This registers a temporary choice (i.e. applying user preferences) for the
198
     * lifetime of this service manager only.
199
     *
200
     * @param string $service the service name being set
201
     * @param array  $choices array of priorities for each of the named service providers
202
     *
203
     * @return void
204
     */
205
    public function registerChoice($service, $choices)
206
    {
207
        $provider = $this->locate($service);
208
        $providers = $provider->getRegistered();
209
        foreach ($providers as $p) {
210
            $name = strtolower($p->getName());
211
            if (isset($choices[$name])) {
212
                $p->setPriority($choices[$name]);
213
            }
214
        }
215
        $provider->sortProviders();
216
    }
217
218
    /**
219
     * listChoices - list choices available for a named service
220
     *
221
     * For MODE_CHOICE services, this can supply an array containing the
222
     * available choices. This array can be used to construct a user form
223
     * to make a choice.
224
     *
225
     * @param string $service the service name being set
226
     *
227
     * @return array of available service provider objects for this service.
228
     */
229
    public function listChoices($service)
230
    {
231
        return $this->locate($service)->getRegistered();
232
    }
233
234
    /**
235
     * locate - create a provider object for a named service, locating all contract implementors
236
     *
237
     * @param string $service the service name being set
238
     *
239
     * @return Provider object for the requested service
240
     */
241 19
    public function locate($service)
242
    {
243 19
        $service = strtolower($service);
244 19
        if (isset($this->services[$service])) {
245
            // service already located
246 17
            $provider = $this->services[$service];
247
        } else {
248 2
            $xoops = \Xoops::getInstance();
249 2
            $provider = new Provider($this, $service);
250 2
            $event = 'core.service.locate.' . $service;
251
            // locate service provider(s)
252
            // In response to trigger message, the contract implementor should register()
253 2
            $xoops->events()->triggerEvent($event, $provider);
254
            // get reference to the list of providers and prioritize it.
255 2
            $registered=$provider->getRegistered();
256 2
            if (count($registered)) {
257
                $choices = $this->providerPrefs[$service] ?? [];
258
                foreach ($registered as $p) {
259
                    $name = strtolower($p->getName());
260
                    if (isset($choices[$name])) {
261
                        $p->setPriority($choices[$name]);
262
                    }
263
                }
264
                $provider->sortProviders();
265
            } else {
266
                // replace with a null provider since no contract implementers were
267 2
                $provider = new NullProvider($this, $service);
268
            }
269 2
            $this->services[$service] = $provider;
270
        }
271
272 19
        return $provider;
273
    }
274
}
275