Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.00% |
38 / 40 |
|
83.33% |
5 / 6 |
CRAP | |
0.00% |
0 / 1 |
Service | |
95.00% |
38 / 40 |
|
83.33% |
5 / 6 |
12 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
invoke | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
resolve | |
91.67% |
22 / 24 |
|
0.00% |
0 / 1 |
5.01 | |||
triggerError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
triggerPre | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
triggerPost | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * Search service. |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2010, 2022. |
9 | * Copyright (C) The National Library of Finland 2019. |
10 | * |
11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License version 2, |
13 | * as published by the Free Software Foundation. |
14 | * |
15 | * This program is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 | * |
24 | * @category VuFind |
25 | * @package Search |
26 | * @author David Maus <maus@hab.de> |
27 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
28 | * @author Aleksi Peebles <aleksi.peebles@helsinki.fi> |
29 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
30 | * @link https://vufind.org |
31 | */ |
32 | |
33 | namespace VuFindSearch; |
34 | |
35 | use Laminas\EventManager\EventManagerInterface; |
36 | use VuFindSearch\Backend\BackendInterface; |
37 | use VuFindSearch\Backend\Exception\BackendException; |
38 | use VuFindSearch\Command\CommandInterface; |
39 | |
40 | /** |
41 | * Search service. |
42 | * |
43 | * @category VuFind |
44 | * @package Search |
45 | * @author David Maus <maus@hab.de> |
46 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
47 | * @author Aleksi Peebles <aleksi.peebles@helsinki.fi> |
48 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
49 | * @link https://vufind.org |
50 | */ |
51 | class Service |
52 | { |
53 | use \VuFindSearch\Feature\SearchBackendEventManagerTrait; |
54 | |
55 | /** |
56 | * Event identifiers. |
57 | * |
58 | * @var string |
59 | */ |
60 | public const EVENT_PRE = 'pre'; |
61 | public const EVENT_POST = 'post'; |
62 | public const EVENT_ERROR = 'error'; |
63 | public const EVENT_RESOLVE = 'resolve'; |
64 | |
65 | /** |
66 | * Cache resolved backends. |
67 | * |
68 | * @var array |
69 | */ |
70 | protected $backends; |
71 | |
72 | /** |
73 | * Constructor. |
74 | * |
75 | * @param ?EventManagerInterface $events Event manager (optional) |
76 | * |
77 | * @return void |
78 | */ |
79 | public function __construct(EventManagerInterface $events = null) |
80 | { |
81 | if (null !== $events) { |
82 | $this->setEventManager($events); |
83 | } |
84 | $this->backends = []; |
85 | } |
86 | |
87 | /** |
88 | * Invoke a command. |
89 | * |
90 | * @param CommandInterface $command Command |
91 | * |
92 | * @return CommandInterface |
93 | */ |
94 | public function invoke(CommandInterface $command) |
95 | { |
96 | // The backend instance is no longer added as an event parameter. |
97 | // All other legacy event parameters are accessible via the command object. |
98 | $args = ['command' => $command]; |
99 | |
100 | $backend = $this->resolve($command->getTargetIdentifier(), $args); |
101 | |
102 | $this->triggerPre($this, $args); |
103 | try { |
104 | $command->execute($backend); |
105 | } catch (BackendException $e) { |
106 | $args['error'] = $e; |
107 | $this->triggerError($this, $args); |
108 | throw $e; |
109 | } |
110 | $this->triggerPost($this, $args); |
111 | |
112 | return $command; |
113 | } |
114 | |
115 | /** |
116 | * Resolve a backend. |
117 | * |
118 | * @param string $backendId Backend name |
119 | * @param array|ArrayAccess $args Service function arguments |
120 | * |
121 | * @return BackendInterface |
122 | * |
123 | * @throws Exception\RuntimeException Unable to resolve backend |
124 | */ |
125 | protected function resolve($backendId, $args) |
126 | { |
127 | if (!isset($this->backends[$backendId])) { |
128 | $response = $this->getEventManager()->triggerUntil( |
129 | function ($o) { |
130 | return $o instanceof BackendInterface; |
131 | }, |
132 | self::EVENT_RESOLVE, |
133 | $this, |
134 | $args |
135 | ); |
136 | if (!$response->stopped()) { |
137 | // We need to construct our error message differently depending |
138 | // on whether or not we have a command object... |
139 | $context = isset($args['command']) |
140 | ? $args['command']->getContext() |
141 | : ($args['context'] ?? 'null'); |
142 | $backendId = isset($args['command']) |
143 | ? $args['command']->getTargetIdentifier() |
144 | : ($args['backend'] ?? $backendId); |
145 | throw new Exception\RuntimeException( |
146 | sprintf( |
147 | 'Unable to resolve backend: %s, %s', |
148 | $context, |
149 | $backendId |
150 | ) |
151 | ); |
152 | } |
153 | $this->backends[$backendId] = $response->last(); |
154 | } |
155 | return $this->backends[$backendId]; |
156 | } |
157 | |
158 | /** |
159 | * Trigger the error event. |
160 | * |
161 | * @param mixed $target Service instance, or error exception for deprecated |
162 | * legacy events |
163 | * @param array $args Event arguments |
164 | * |
165 | * @return void |
166 | */ |
167 | public function triggerError($target, $args) |
168 | { |
169 | $this->getEventManager()->trigger(self::EVENT_ERROR, $target, $args); |
170 | } |
171 | |
172 | /** |
173 | * Trigger the pre event. |
174 | * |
175 | * @param mixed $target Service instance, or backend instance for deprecated |
176 | * legacy events |
177 | * @param array $args Event arguments |
178 | * |
179 | * @return void |
180 | */ |
181 | protected function triggerPre($target, $args) |
182 | { |
183 | $this->getEventManager()->trigger(self::EVENT_PRE, $target, $args); |
184 | } |
185 | |
186 | /** |
187 | * Trigger the post event. |
188 | * |
189 | * @param mixed $target Service instance, or backend response for deprecated |
190 | * legacy events |
191 | * @param array $args Event arguments |
192 | * |
193 | * @return void |
194 | */ |
195 | protected function triggerPost($target, $args) |
196 | { |
197 | $this->getEventManager()->trigger(self::EVENT_POST, $target, $args); |
198 | } |
199 | } |