This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace izzum\statemachine\persistence; |
||
3 | use izzum\statemachine\loader\Loader; |
||
4 | use izzum\statemachine\StateMachine; |
||
5 | use izzum\statemachine\Context; |
||
6 | use izzum\statemachine\loader\LoaderArray; |
||
7 | use izzum\statemachine\loader\LoaderData; |
||
8 | use izzum\statemachine\Exception; |
||
9 | use izzum\statemachine\Identifier; |
||
10 | use izzum\statemachine\Transition; |
||
11 | use izzum\statemachine\loader\JSON; |
||
12 | /** |
||
13 | * Redis is an open source advanced key-value (nosql database) cache and store using |
||
14 | * datastructures. |
||
15 | * |
||
16 | * Since redis has no schemas (as a nosql db), it should store data based on the way you want to retrieve data. |
||
17 | * There will be multiple views on the data which should make sense for a lot of use cases (counters, |
||
18 | * sets, sorted sets, lists, hashes etc) to retrieve the data. |
||
19 | * All keys that izzum uses in redis can be found in this classes' constants with a 'KEY_' prefix. |
||
20 | * |
||
21 | * This class uses the php redis module and your php build should be setup with this module loaded. |
||
22 | * |
||
23 | * An instance uses a redis key prefix of 'izzum:' by default. but this can be set to whatever you like. |
||
24 | * |
||
25 | * . |
||
26 | * You can even set the prefix key to something else temporarily for loading the configuration data and |
||
27 | * then set it to another prefix for writing state data. This allows you to store multiple machine |
||
28 | * configurations under different prefixes so you can load different machines from different places, |
||
29 | * which facilitates multiple dev teams working on configuration of different machines without them |
||
30 | * overwriting other teams' definitions. |
||
31 | * |
||
32 | * The configuration of statemachines is a JSON string. The specification of the JSON string can |
||
33 | * be found in izzum\statemachine\loader\JSON::getJSONSchema. see asset/json/json.schema |
||
34 | * |
||
35 | * Internally, this class uses the JSON loader to load the configuration. It can be set up to |
||
36 | * store multiple configurations under multiple keys. |
||
37 | * |
||
38 | * You can use the normal redis connection settings to connect to redis. |
||
39 | * |
||
40 | * @link http://redis.io |
||
41 | * @link https://github.com/nicolasff/phpredis a php module for redis you'll need to use this class. |
||
42 | * you need to install the php module and make it available to php: |
||
43 | * - debian/ubuntu: via the apt package manager: apt-get install php5-redis |
||
44 | * - osx: use homebrew |
||
45 | * - install homebrew php https://github.com/Homebrew/homebrew-php |
||
46 | * - install php5-redis: brew install php55-redis |
||
47 | * |
||
48 | * @link http://redis.io/commands/sort#using-hashes-in-codebycode-and-codegetcode to get information |
||
49 | * about how to query a set or list of referenced keys |
||
50 | * @link https://stackoverflow.com/questions/10155398/getting-multiple-key-values-from-redis methods of retrieval of data in redis |
||
51 | * @link https://github.com/antirez/lamernews redis implementation with interesting datastructures (by the redis author) |
||
52 | * |
||
53 | * @author Rolf Vreijdenberger |
||
54 | * |
||
55 | */ |
||
56 | class Redis extends Adapter implements Loader { |
||
57 | |||
58 | const DATABASE_DEFAULT = 0;//default database to use |
||
59 | const KEY_PREFIX_IZZUM = 'izzum:';//default key prefix |
||
60 | |||
61 | /** |
||
62 | * default key for configuration of machines |
||
63 | */ |
||
64 | const KEY_CONFIGURATION = 'configuration'; |
||
65 | |||
66 | /** |
||
67 | * configuration:<machine-name> |
||
68 | */ |
||
69 | const KEY_CONFIGURATION_SPECIFIC = '%s:%s'; |
||
70 | /** |
||
71 | * set of entities in a machine: <machine> |
||
72 | */ |
||
73 | const KEY_ENTITYIDS = 'entities:%s:ids'; |
||
74 | /** |
||
75 | * set of entities per state: <machine>, <state> |
||
76 | */ |
||
77 | const KEY_CURRENT_STATES = 'entities:%s:states:%s'; |
||
78 | |||
79 | /** |
||
80 | * state of an entity: <machine>, <id> |
||
81 | */ |
||
82 | const KEY_ENTITY_STATE = 'entities:%s:state:%s'; |
||
83 | |||
84 | |||
85 | /** |
||
86 | * this key stores the count of all transitions and is used as the id that references the canonical form. |
||
87 | * transitions are added by incrementing this counter and using that id as key to the entries of the canonical forms |
||
88 | * of the transitions (stored as a redis hash) |
||
89 | */ |
||
90 | const KEY_COUNTERS_TRANSITIONS_ALL = 'counters:transitions:all'; |
||
91 | |||
92 | /** |
||
93 | * the keys that stores the canonical form of the transitions, storing the transition data. |
||
94 | * all other transitions will reference the id/counter of the canonical form |
||
95 | */ |
||
96 | const KEY_TRANSITIONS_CANONICAL = 'transitions:canonical:%s';//hash: $counter (KEY_COUNTERS_TRANSITIONS_ALL) |
||
0 ignored issues
–
show
|
|||
97 | |||
98 | /** |
||
99 | * the next keys store id's in sorted sets, scored by timestamp. the id's reference the canonical form. |
||
100 | * the keys provide different views on the data that can easily be retrieved by using: |
||
101 | * - sort: the sort command allows you to reference multiple transitions as referenced in a list or set, in one command |
||
102 | * - zcount: count the elements in a sorted set. this gives you an easy count on the number of transitions per |
||
103 | * machine, state, entity etc. since those are all stored. |
||
104 | */ |
||
105 | const KEY_TRANSITIONS_ALL = 'transitions:all:normal';//sorted set |
||
106 | const KEY_TRANSITIONS_MACHINES = 'transitions:machines:normal:%s';//sorted set: $machine |
||
107 | const KEY_TRANSITIONS_STATES = 'transitions:states:normal:%s:%s';//sorted set: $machine, $state |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
38% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
108 | const KEY_TRANSITIONS_ENTITIES = 'transitions:entities:normal:%s:%s';//sorted set: $machine, $entity_id |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
38% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
109 | const KEY_TRANSITIONS_ALL_FAILED = 'transitions:all:failed';//sorted set |
||
110 | const KEY_TRANSITIONS_MACHINES_FAILED = 'transitions:machines:failed:%s';//sorted set:$machine |
||
111 | const KEY_TRANSITIONS_STATES_FAILED = 'transitions:states:failed:%s:%s';//sorted set: $machine, $state |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
38% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
112 | const KEY_TRANSITIONS_ENTITIES_FAILED = 'transitions:entities:failed:%s:%s';//sorted set: $machine, $entity_id |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
38% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
113 | |||
114 | |||
115 | |||
116 | private $host; |
||
117 | private $port; |
||
118 | private $timeout; |
||
119 | private $reserved; |
||
120 | private $retry; |
||
121 | private $socket; |
||
122 | private $password; |
||
123 | private $database; |
||
124 | private $prefix; |
||
125 | private $configuration_key; |
||
126 | |||
127 | /** |
||
128 | * connected and optionally authenticated redis connection. |
||
129 | * @var \Redis |
||
130 | */ |
||
131 | private $redis; |
||
132 | |||
133 | |||
134 | /** |
||
135 | * The constructor accepts default connection parameters. |
||
136 | * |
||
137 | * You can also use an existing \Redis instance used by your application. |
||
138 | * Just construct without parameters and call 'setConnection($instance)' before doing anything else. |
||
139 | * |
||
140 | * You can also use a unix domain socket. Just construct without parameters |
||
141 | * and call 'setUnixDomainSocket' before doing anything else. |
||
142 | * |
||
143 | * @param string $host optional |
||
144 | * @param int $port optional |
||
145 | * @param float $timeout value in seconds. default is 0 meaning unlimited |
||
146 | * @param string $reserved should be NULL if $retry is specified |
||
147 | * @param int $retry value in milliseconds |
||
148 | */ |
||
149 | 3 | public function __construct($host = '127.0.0.1', $port = 6379, $timeout = 0, $reserved = null, $retry = null) |
|
150 | { |
||
151 | |||
152 | 3 | $this->host = $host; |
|
153 | 3 | $this->port = $port; |
|
154 | 3 | $this->timeout = $timeout; |
|
155 | 3 | $this->reserved = $reserved; |
|
156 | 3 | $this->retry = $retry; |
|
157 | 3 | $this->socket = null; |
|
158 | 3 | $this->setPrefix(self::KEY_PREFIX_IZZUM); |
|
159 | 3 | $this->setConfigurationKey(self::KEY_CONFIGURATION); |
|
160 | 3 | $this->setDatabase(self::DATABASE_DEFAULT); |
|
161 | |||
162 | |||
163 | 3 | } |
|
164 | |||
165 | public function setUnixDomainSocket($socket) |
||
166 | { |
||
167 | $this->socket = $socket; |
||
168 | $this->host = null; |
||
169 | $this->port = null; |
||
170 | $this->timeout = null; |
||
171 | $this->reserved = null; |
||
172 | $this->retry = null; |
||
173 | $this->socket = null; |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * set password to authenticate to the redis server |
||
178 | * @param string $password |
||
179 | */ |
||
180 | public function setPassword($password) { |
||
181 | $this->password = $password; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * set the redis database. in case there is an active connection, it switches the database. |
||
186 | * @param int $database a redis database is an integer starting from 0 (the default) |
||
187 | */ |
||
188 | 3 | public function setDatabase($database) { |
|
189 | 3 | if($this->redis) { |
|
190 | $this->redis->select($database); |
||
191 | } |
||
192 | 3 | $this->database = $database; |
|
193 | 3 | } |
|
194 | |||
195 | /** |
||
196 | * set the redis connection explicitely, useful if you want to share the |
||
197 | * redis instance when it is created outside this class. |
||
198 | * @param \Redis $redis a connected (and authenticated) redis instance |
||
199 | */ |
||
200 | public function setConnection(\Redis $redis) |
||
201 | { |
||
202 | $this->redis = $redis; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * set the key prefix to be used for all redis keys |
||
207 | * @param string $prefix |
||
208 | */ |
||
209 | 3 | final public function setPrefix($prefix) { |
|
210 | 3 | if($this->redis) { |
|
211 | 1 | $this->redis->setOption(\Redis::OPT_PREFIX, $prefix); |
|
212 | 1 | } |
|
213 | 3 | $this->prefix = $prefix; |
|
214 | 3 | } |
|
215 | |||
216 | /** |
||
217 | * set the configuration key to be used for storing a json string of machine configurations. |
||
218 | * @param string $key |
||
219 | */ |
||
220 | 3 | final public function setConfigurationKey($key) { |
|
221 | 3 | $this->configuration_key = $key; |
|
222 | 3 | } |
|
223 | |||
224 | /** |
||
225 | * get the configuration key used for storing a json string of machine configurations. |
||
226 | * @return string $key |
||
227 | */ |
||
228 | 3 | final public function getConfigurationKey() { |
|
229 | 3 | return $this->configuration_key; |
|
230 | } |
||
231 | |||
232 | /** |
||
233 | * get the prefix for all keys used |
||
234 | * @return string |
||
235 | */ |
||
236 | 3 | final public function getPrefix() |
|
237 | { |
||
238 | 3 | return $this->prefix; |
|
239 | } |
||
240 | |||
241 | /** |
||
242 | * Gets a lazy loaded \Redis instance that is connected and optionally authenticated. |
||
243 | * |
||
244 | * @throws Exception |
||
245 | * @return \Redis |
||
246 | */ |
||
247 | 3 | public function getRedis() { |
|
248 | //lazy loaded connection |
||
249 | try { |
||
250 | 3 | if($this->redis === null) { |
|
251 | 3 | $this->redis = new \Redis(); |
|
252 | 3 | if($this->socket) { |
|
253 | $connected = $this->redis->connect($this->socket); |
||
254 | } else { /* default connection with different parameters */ |
||
255 | 3 | if($this->retry !== null) { |
|
256 | $connected = $this->redis->connect($this->host, $this->port, $this->timeout, null, $this->retry); |
||
257 | } else { |
||
258 | 3 | if($this->reserved !== null) { |
|
259 | $connected = $this->redis->connect($this->host, $this->port, $this->timeout, $this->reserved); |
||
260 | } else { |
||
261 | 3 | $connected = $this->redis->connect($this->host, $this->port, $this->timeout); |
|
262 | } |
||
263 | } |
||
264 | } |
||
265 | 3 | if(!$connected) { |
|
266 | $this->redis = null; |
||
267 | throw new Exception('connection not made', Exception::PERSISTENCE_FAILED_TO_CONNECT); |
||
268 | } |
||
269 | 3 | if($this->password) { |
|
270 | $authenticated = $this->redis->auth($this->password); |
||
271 | if(!$authenticated) { |
||
272 | throw new Exception('authentication failed', Exception::PERSISTENCE_FAILED_TO_CONNECT); |
||
273 | } |
||
274 | } |
||
275 | //set the database |
||
276 | 3 | $this->redis->select($this->database); |
|
277 | 3 | $this->redis->setOption(\Redis::OPT_PREFIX, $this->getPrefix()); |
|
278 | //hook for subclass |
||
279 | 3 | $this->onConnect(); |
|
280 | 3 | } |
|
281 | |||
282 | 3 | return $this->redis; |
|
283 | |||
284 | } catch (\Exception $e) { |
||
285 | throw new Exception( |
||
286 | sprintf("error creating Redis connection: [%s]", |
||
287 | $e->getMessage()), |
||
288 | Exception::PERSISTENCE_FAILED_TO_CONNECT); |
||
289 | } |
||
290 | |||
291 | } |
||
292 | |||
293 | /** |
||
294 | * A hook to use in a subclass. |
||
295 | * you can do you initial setup here if you like. |
||
296 | */ |
||
297 | 3 | protected function onConnect() { |
|
298 | //override if necessary |
||
299 | 3 | } |
|
300 | |||
301 | /** |
||
302 | * {@inheritDoc} |
||
303 | */ |
||
304 | 2 | public function processGetState(Identifier $identifier) { |
|
305 | 2 | $redis = $this->getRedis(); |
|
306 | try { |
||
307 | //get state from key |
||
308 | 2 | $key = sprintf(self::KEY_ENTITY_STATE, $identifier->getMachine(), $identifier->getEntityId()); |
|
309 | 2 | $state = $redis->get($key); |
|
310 | 2 | } catch (\Exception $e) { |
|
311 | throw new Exception(sprintf('getting current state failed: [%s]', |
||
312 | $e->getMessage()), Exception::PERSISTENCE_LAYER_EXCEPTION); |
||
313 | } |
||
314 | 2 | if(!$state) { |
|
315 | throw new Exception(sprintf('no state found for [%s]. ' |
||
316 | . 'Did you "$machine->add()" it to the persistence layer?', |
||
317 | $identifier->getId(true)), |
||
318 | Exception::PERSISTENCE_LAYER_EXCEPTION); |
||
319 | } |
||
320 | 2 | return $state; |
|
321 | } |
||
322 | |||
323 | |||
324 | /** |
||
325 | * {@inheritDoc} |
||
326 | */ |
||
327 | 2 | public function isPersisted(Identifier $identifier) { |
|
328 | try { |
||
329 | 2 | $redis = $this->getRedis(); |
|
330 | //get key from known entity ids set |
||
331 | 2 | $key = sprintf(self::KEY_ENTITYIDS, $identifier->getMachine()); |
|
332 | 2 | return $redis->sismember($key, $identifier->getEntityId()); |
|
333 | } catch (\Exception $e) { |
||
334 | throw new Exception( |
||
335 | sprintf('getting persistence info failed: [%s]', |
||
336 | $e->getMessage()), Exception::PERSISTENCE_LAYER_EXCEPTION); |
||
337 | } |
||
338 | } |
||
339 | |||
340 | |||
341 | /** |
||
342 | * {@inheritDoc} |
||
343 | */ |
||
344 | 2 | public function insertState(Identifier $identifier, $state, $message = null) |
|
345 | { |
||
346 | 2 | $redis = $this->getRedis(); |
|
347 | try { |
||
348 | 2 | $redis->multi(\Redis::MULTI); |
|
349 | //add to set of known id's |
||
350 | 2 | $key = sprintf(self::KEY_ENTITYIDS, $identifier->getMachine()); |
|
351 | 2 | $redis->sadd($key, $identifier->getEntityId()); |
|
352 | //set the state |
||
353 | 2 | $key = sprintf(self::KEY_ENTITY_STATE, $identifier->getMachine(), $identifier->getEntityId()); |
|
354 | 2 | $redis->set($key, $state); |
|
355 | //set on current states set |
||
356 | 2 | $key = sprintf(self::KEY_CURRENT_STATES, $identifier->getMachine(), $state); |
|
357 | 2 | $redis->sadd($key, $identifier->getEntityId()); |
|
358 | 2 | $redis->exec(); |
|
359 | |||
360 | 2 | } catch (\Exception $e) { |
|
361 | $redis->discard(); |
||
362 | throw new Exception(sprintf('query for inserting state failed: [%s]', |
||
363 | $e->getMessage()), |
||
364 | Exception::PERSISTENCE_LAYER_EXCEPTION); |
||
365 | } |
||
366 | 2 | } |
|
367 | |||
368 | |||
369 | |||
370 | /** |
||
371 | * {@inheritDoc} |
||
372 | */ |
||
373 | 2 | public function updateState(Identifier $identifier, $state, $message = null) |
|
374 | { |
||
375 | |||
376 | 2 | $redis = $this->getRedis(); |
|
377 | try { |
||
378 | //first, get the current state |
||
379 | 2 | $key = sprintf(self::KEY_ENTITY_STATE, $identifier->getMachine(), $identifier->getEntityId()); |
|
380 | 2 | $current = $redis->get($key); |
|
381 | |||
382 | |||
383 | //now that we have the current state, start multi in pipeline mode for faster execution (single server roundtrip) |
||
384 | 2 | $redis->multi(\Redis::PIPELINE); |
|
385 | //remove from current state set |
||
386 | 2 | $key = sprintf(self::KEY_CURRENT_STATES, $identifier->getMachine(), $current); |
|
387 | 2 | $redis->srem($key, $identifier->getEntityId()); |
|
388 | //set the new state |
||
389 | 2 | $key = sprintf(self::KEY_ENTITY_STATE, $identifier->getMachine(), $identifier->getEntityId()); |
|
390 | 2 | $redis->set($key, $state); |
|
391 | //set on current states set |
||
392 | 2 | $key = sprintf(self::KEY_CURRENT_STATES, $identifier->getMachine(), $state); |
|
393 | 2 | $redis->sadd($key, $identifier->getEntityId()); |
|
394 | //execute the sequence of redis commands |
||
395 | 2 | $redis->exec(); |
|
396 | |||
397 | 2 | } catch (\Exception $e) { |
|
398 | $redis->discard(); |
||
399 | throw new Exception(sprintf('updating state failed: [%s]', |
||
400 | $e->getMessage()), |
||
401 | Exception::PERSISTENCE_LAYER_EXCEPTION); |
||
402 | } |
||
403 | 2 | } |
|
404 | |||
405 | /** |
||
406 | * {@inheritDoc} |
||
407 | */ |
||
408 | 2 | public function addHistory(Identifier $identifier, $state, $message = null, $is_exception = false) |
|
409 | { |
||
410 | 2 | $redis = $this->getRedis(); |
|
411 | try { |
||
412 | 2 | $machine = $identifier->getMachine(); |
|
413 | 2 | $entity_id = $identifier->getEntityId(); |
|
414 | |||
415 | //counter for the number of transitions. |
||
416 | 2 | $key = self::KEY_COUNTERS_TRANSITIONS_ALL; |
|
417 | //this will function as the id of the transition data, stored as a redis hash |
||
418 | 2 | $counter = $redis->incr($key); |
|
419 | |||
420 | //now that we have the counter, start a redis multi command in pipeline mode |
||
421 | 2 | $redis->multi(\Redis::PIPELINE); |
|
422 | |||
423 | //create the record for the transition to store in a redis hash |
||
424 | 2 | $timestamp = time(); |
|
425 | 2 | $record = array(); |
|
426 | 2 | $record['state'] = $state; |
|
427 | 2 | $record['machine'] = $machine; |
|
428 | 2 | $record['entity_id'] = $entity_id; |
|
429 | 2 | if($message) { |
|
430 | 2 | if(is_string($message)) { |
|
431 | 2 | $info = new \stdClass(); |
|
432 | 2 | $info->message = $message; |
|
433 | 2 | $message = $info; |
|
434 | 2 | } |
|
435 | //always json_encode so we can pass objects as messages |
||
436 | 2 | $message = json_encode($message); |
|
437 | 2 | } |
|
438 | 2 | $record['message'] = $message; |
|
439 | 2 | $record['timestamp'] = $timestamp; |
|
440 | 2 | $record['datetime'] = date('Y-m-d H:i:s', $timestamp);//ISO_8601 |
|
441 | 2 | $record['exception'] = $is_exception ? 1 : 0; |
|
442 | 2 | $record['id'] = $counter; |
|
443 | |||
444 | /* |
||
445 | * set the canonical form of the transition as a hash with the counter as the id. |
||
446 | * This allows you to get data from the canonical from by storing a reference to the transaction id |
||
447 | * in other lists or sets (memory optimization) via: |
||
448 | * sort izzum:transitions:all:normal by nosort get izzum:transitions:canonical:*->message STORE mymessages |
||
449 | * @see http://redis.io/commands/sort |
||
450 | */ |
||
451 | |||
452 | 2 | $key_hash_id = sprintf(self::KEY_TRANSITIONS_CANONICAL, $counter); |
|
453 | 2 | $redis->hmset($key_hash_id, $record); |
|
454 | |||
455 | |||
456 | //store all transitions referencing the id of the canonical form in sorted sets (keyed by timestamp), |
||
457 | //set manipulations are powerful in redis (diff, union, intersect etc with the exceptions for example) |
||
458 | 2 | $key = self::KEY_TRANSITIONS_ALL; |
|
459 | 2 | $redis->zadd($key, $timestamp, $counter); |
|
460 | 2 | $key = sprintf(self::KEY_TRANSITIONS_MACHINES, $machine); |
|
461 | 2 | $redis->zadd($key, $timestamp, $counter); |
|
462 | 2 | $key = sprintf(self::KEY_TRANSITIONS_STATES, $machine, $state); |
|
463 | 2 | $redis->zadd($key, $timestamp, $counter); |
|
464 | 2 | $key = sprintf(self::KEY_TRANSITIONS_ENTITIES, $machine, $entity_id); |
|
465 | 2 | $redis->zadd($key, $timestamp, $counter); |
|
466 | |||
467 | |||
468 | 2 | if($is_exception) { |
|
469 | //store all failed transitions referencing the id of the canonical form in sets |
||
470 | 1 | $key = self::KEY_TRANSITIONS_ALL_FAILED; |
|
471 | 1 | $redis->zadd($key, $timestamp, $counter); |
|
472 | 1 | $key = sprintf(self::KEY_TRANSITIONS_MACHINES_FAILED, $machine); |
|
473 | 1 | $redis->zadd($key, $timestamp, $counter); |
|
474 | 1 | $key = sprintf(self::KEY_TRANSITIONS_STATES_FAILED, $machine, $state); |
|
475 | 1 | $redis->zadd($key, $timestamp, $counter); |
|
476 | 1 | $key = sprintf(self::KEY_TRANSITIONS_ENTITIES_FAILED, $machine, $entity_id); |
|
477 | 1 | $redis->zadd($key, $timestamp, $counter); |
|
478 | 1 | } |
|
479 | //execute the sequence of redis commands |
||
480 | 2 | $redis->exec(); |
|
481 | |||
482 | 2 | } catch (\Exception $e) { |
|
483 | $redis->discard(); |
||
484 | throw new Exception(sprintf('adding history failed: [%s]', |
||
485 | $e->getMessage()), |
||
486 | Exception::PERSISTENCE_LAYER_EXCEPTION); |
||
487 | } |
||
488 | 2 | } |
|
489 | |||
490 | /** |
||
491 | * {@inheritDoc} |
||
492 | */ |
||
493 | 2 | public function getEntityIds($machine, $state = null) { |
|
494 | 2 | $output = array(); |
|
495 | try { |
||
496 | 2 | $redis = $this->getRedis(); |
|
497 | 2 | if($state !== null) { |
|
498 | //get from set of entities per state |
||
499 | 1 | $key = sprintf(self::KEY_CURRENT_STATES, $machine, $state); |
|
500 | 1 | $output = $redis->smembers($key); |
|
501 | 1 | } else { |
|
502 | //get state directly |
||
503 | 2 | $key = sprintf(self::KEY_ENTITYIDS, $machine); |
|
504 | 2 | $output = $redis->smembers($key); |
|
505 | } |
||
506 | 2 | } catch (\Exception $e) { |
|
507 | throw new Exception($e->getMessage(), |
||
508 | Exception::PERSISTENCE_LAYER_EXCEPTION, $e); |
||
509 | } |
||
510 | 2 | return $output; |
|
511 | } |
||
512 | |||
513 | /** |
||
514 | * {@inheritDoc} |
||
515 | * Load the statemachine with data from a JSON string. |
||
516 | * the JSON string is stored at the redis key '<prefix:>configuration' by default. |
||
517 | * you can alter the configuration key by using Redis::setPrefix() and Redis::setConfigurationKey() |
||
518 | * |
||
519 | * First, the key '<prefix>:configuration:<machine-name>' is checked for existence. |
||
520 | * If it exists, take the configuration from that key, else take the configuration form |
||
521 | * the '<prefix>:configuration' key. |
||
522 | * |
||
523 | * This method can be overriden in a subclass to use another loader when |
||
524 | * the data is stored in redis in YAML or XML form for example. |
||
525 | * You could use the ReaderWriterDelegator to use another source to load the configuration from. |
||
526 | */ |
||
527 | 3 | public function load(StateMachine $statemachine) { |
|
528 | //use the JSON loader to load the configuration (see the json schema we expect in JSON::getJSONSchema) |
||
529 | 3 | $key = $this->getConfigurationKey(); |
|
530 | 3 | $redis = $this->getRedis(); |
|
531 | 3 | $specific_key = sprintf(self::KEY_CONFIGURATION_SPECIFIC, $key, $statemachine->getContext()->getMachine()); |
|
532 | 3 | if($redis->exists($specific_key)){ |
|
533 | 1 | $key = $specific_key; |
|
534 | 1 | } |
|
535 | 3 | $loader = new JSON($this->getRedis()->get($key)); |
|
536 | 3 | $count = $loader->load($statemachine); |
|
537 | 3 | return $count; |
|
538 | } |
||
539 | |||
540 | |||
541 | /** |
||
542 | * do some cleanup |
||
543 | */ |
||
544 | public function __destruct() |
||
545 | { |
||
546 | try { |
||
547 | if($this->redis) { |
||
548 | $this->redis->close(); |
||
549 | $this->redis = null; |
||
550 | } |
||
551 | } catch (\Exception $e) { |
||
552 | //nothing we can do about it... |
||
553 | } |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * very very dumb proxy to redis connection. should only be used for testing. |
||
558 | * calls methods on a https://github.com/phpredis/phpredis instance. |
||
559 | * |
||
560 | * some method calls will only accept an array as the second argument (like hmset) |
||
561 | * |
||
562 | * This makes it useful to test the redis commands or just use this class as an interface to redis. |
||
563 | * |
||
564 | * @param string $name name of the method to route to the active redis connection |
||
565 | * @param mixed $arguments |
||
566 | * @return mixed |
||
567 | */ |
||
568 | 3 | public function __call($name, $arguments) |
|
569 | { |
||
570 | //call the method with $name on the \Redis instance |
||
571 | 3 | return call_user_func_array(array($this->getRedis(), $name), $arguments); |
|
572 | } |
||
573 | |||
574 | 2 | public function toString() |
|
575 | { |
||
576 | 2 | return get_class($this) . ' redis://'. $this->host . ':' . $this->port . '/' . $this->database; |
|
577 | } |
||
578 | |||
579 | 2 | public function __toString() |
|
580 | { |
||
581 | 2 | return $this->toString(); |
|
582 | } |
||
583 | |||
584 | } |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.