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 Xoops\Core\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 <richard@geekwright.com>
22: * @copyright 2013-2014 The XOOPS Project https://github.com/XOOPS/XoopsCore
23: * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
24: * @version Release: 1.0
25: * @link http://xoops.org
26: * @since 2.6.0
27: */
28: class Manager
29: {
30: /**
31: * Service Mode constant - Exclusive mode where only one located service
32: * will be used.
33: */
34: const MODE_EXCLUSIVE = 1;
35:
36: /**
37: * Service Mode constant - Choice mode where one service from potentially
38: * many located services will be used. The service dispatched will be selected
39: * by system default.
40: */
41: const MODE_CHOICE = 2;
42:
43: /**
44: * Service Mode constant - Choice mode where one service from potentially many
45: * located services will be used. The service dispatched will be selected by user
46: * preference, or system default if no user valid preference is available.
47: */
48: const MODE_PREFERENCE = 4;
49:
50: /**
51: * Service Mode constant - Multiple mode where all located services will be
52: * dispatched in priority order.
53: */
54: const MODE_MULTIPLE = 8;
55:
56: /**
57: * Provider priorities
58: */
59: const PRIORITY_SELECTED = 0;
60: const PRIORITY_HIGH = 1;
61: const PRIORITY_MEDIUM = 5;
62: const PRIORITY_LOW = 9;
63:
64: /**
65: * Services registry - array keyed on service name, with provider object as value
66: *
67: * @var array
68: */
69: protected $services = array();
70:
71: /**
72: * Provider Preferences - array keyed on service name, where each element is
73: * an array of provider name => priority entries
74: *
75: * @var array|null
76: */
77: protected $providerPrefs = null;
78:
79: /**
80: * @var string config file with provider prefs
81: */
82: private $providerPrefsFilename = 'var/configs/system_provider_prefs.yml';
83:
84: /**
85: * @var string config cache key
86: */
87: private $providerPrefsCacheKey = 'system/provider/prefs';
88:
89: /**
90: * __construct
91: */
92: protected function __construct()
93: {
94: $this->providerPrefs = $this->readProviderPrefs();
95: }
96:
97: /**
98: * Allow one instance only!
99: *
100: * @return Manager instance
101: */
102: public static function getInstance()
103: {
104: static $instance = false;
105:
106: if (!$instance) {
107: $instance = new Manager();
108: }
109:
110: return $instance;
111: }
112:
113: /**
114: * readYamlProviderPrefs - read configured provider preferences from file
115: *
116: * @return array of configured provider preferences
117: */
118: public function readYamlProviderPrefs()
119: {
120: $xoops = \Xoops::getInstance();
121:
122: $providerPrefs = array();
123:
124: try {
125: $file = $xoops->path($this->providerPrefsFilename);
126: if (file_exists($file)) {
127: $providerPrefs = Yaml::read($xoops->path($file));
128: }
129: if (empty($providerPrefs)) {
130: $providerPrefs = array();
131: }
132: } catch (\Exception $e) {
133: $xoops->events()->triggerEvent('core.exception', $e);
134: $providerPrefs = array();
135: }
136: return $providerPrefs;
137: }
138:
139: /**
140: * readProviderPrefs - read configured provider preferences from cache
141: *
142: * @return array of configured provider preferences
143: */
144: protected function readProviderPrefs()
145: {
146: $xoops = \Xoops::getInstance();
147: $providerPrefs = $xoops->cache()->cacheRead(
148: $this->providerPrefsCacheKey,
149: array($this, 'readYamlProviderPrefs')
150: );
151: return $providerPrefs;
152: }
153:
154: /**
155: * saveProviderPrefs - record array of provider preferences in config file, and
156: * update cache
157: *
158: * @param array $providerPrefs array of provider preferences to save
159: *
160: * @return void
161: */
162: protected function saveProviderPrefs($providerPrefs)
163: {
164: if (is_array($providerPrefs)) {
165: $xoops = \Xoops::getInstance();
166: try {
167: Yaml::save($providerPrefs, $xoops->path($this->providerPrefsFilename));
168: $xoops->cache()->write($this->providerPrefsCacheKey, $providerPrefs);
169: } catch (\Exception $e) {
170: $xoops->events()->triggerEvent('core.exception', $e);
171: }
172: }
173: }
174:
175: /**
176: * saveChoice - record priority choices for service providers
177: *
178: * This registers a permanent choice (i.e. setting system default) that will
179: * persist after the lifetime of this service manager.
180: *
181: * @param string $service the service name being set
182: * @param array $choices array of priorities for each of the named service providers
183: *
184: * @return void
185: */
186: public function saveChoice($service, $choices)
187: {
188: // read current preferences
189: $prefs = $this->readProviderPrefs();
190: // replace prefs for selected service
191: $prefs[$service] = $choices;
192: // save the changes
193: $this->saveProviderPrefs($prefs);
194: // apply to current manager instance
195: $this->registerChoice($service, $choices);
196: }
197:
198: /**
199: * registerChoice - record priority choices for service providers
200: *
201: * This registers a temporary choice (i.e. applying user preferences) for the
202: * lifetime of this service manager only.
203: *
204: * @param string $service the service name being set
205: * @param array $choices array of priorities for each of the named service providers
206: *
207: * @return void
208: */
209: public function registerChoice($service, $choices)
210: {
211: $provider = $this->locate($service);
212: $providers = $provider->getRegistered();
213: foreach ($providers as $p) {
214: $name = strtolower($p->getName());
215: if (isset($choices[$name])) {
216: $p->setPriority($choices[$name]);
217: }
218: }
219: $provider->sortProviders();
220: }
221:
222: /**
223: * listChoices - list choices availabe for a named service
224: *
225: * For MODE_CHOICE services, this can supply an array containing the
226: * available choices. This array can be used to construct a user form
227: * to make a choice.
228: *
229: * @param string $service the service name being set
230: *
231: * @return array of available service provider objects for this service.
232: */
233: public function listChoices($service)
234: {
235: $providers = $this->locate($service)->getRegistered();
236: return $providers;
237: }
238:
239: /**
240: * locate - create a provider object for a named service, locating all contract implementors
241: *
242: * @param string $service the service name being set
243: *
244: * @return Provider object for the requested service
245: */
246: public function locate($service)
247: {
248: $service = strtolower($service);
249: if (isset($this->services[$service])) {
250: // service already located
251: $provider = $this->services[$service];
252: } else {
253: $xoops = \Xoops::getInstance();
254: $provider = new Provider($this, $service);
255: $event = 'core.service.locate.' . $service;
256: // locate service provider(s)
257: // In response to trigger message, the contract implementor should register()
258: $xoops->events()->triggerEvent($event, $provider);
259: // get reference to the list of providers and prioritize it.
260: $registered=$provider->getRegistered();
261: if (count($registered)) {
262: $choices = isset($this->providerPrefs[$service]) ? $this->providerPrefs[$service] : array();
263: foreach ($registered as $p) {
264: $name = strtolower($p->getName());
265: if (isset($choices[$name])) {
266: $p->setPriority($choices[$name]);
267: }
268: }
269: $provider->sortProviders();
270: } else {
271: // replace with a null provider since no contract implementers were
272: $provider = new NullProvider($this, $service);
273: }
274: $this->services[$service] = $provider;
275: }
276:
277: return $provider;
278: }
279: }
280: