Completed
Pull Request — master (#43)
by Greg
02:21
created

HookManager   D

Complexity

Total Complexity 87

Size/Duplication

Total Lines 596
Duplicated Lines 4.7 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 87
lcom 1
cbo 11
dl 28
loc 596
rs 4.5142
c 0
b 0
f 0

48 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A getAllHooks() 0 4 1
A add() 0 7 2
A recordHookOptions() 0 4 1
A getNames() 0 9 1
A getNamesUsingCommands() 0 7 1
A getClassNameFromCallback() 0 8 2
A addInitializeHook() 0 4 1
A addOptionHook() 0 4 1
A addInteractor() 0 4 1
A addPreValidator() 0 4 1
A addValidator() 0 4 1
A addPreCommandHook() 0 4 1
A addPostCommandHook() 0 4 1
A addResultProcessor() 0 4 1
A addAlterResult() 0 4 1
A addStatusDeterminer() 0 4 1
A addOutputExtractor() 0 4 1
A initializeHook() 0 10 2
A optionsHook() 0 12 2
A getHookOptionsForCommand() 0 7 2
A interact() 0 11 2
A validateArguments() 14 14 4
A alterResult() 0 13 3
A determineStatusCode() 0 20 4
A extractOutput() 0 16 4
A getCommandEventHooks() 0 4 1
A getInitializeHooks() 0 4 1
A getOptionHooks() 0 4 1
A getInteractors() 0 4 1
A getValidators() 7 7 1
A getProcessResultHooks() 7 7 1
A getAlterResultHooks() 0 4 1
A getStatusDeterminers() 0 4 1
A getOutputExtractors() 0 4 1
A getHooks() 0 11 2
A get() 0 10 3
A getHook() 0 7 2
A callInjectConfigurationHook() 0 9 3
A callOptionHook() 0 9 3
A callInteractor() 0 9 3
A callValidator() 0 12 3
A callProcessor() 0 17 4
A callDeterminer() 0 9 3
A callExtractor() 0 9 3
A callCommandEventHooks() 0 12 3
A findAndAddHookOptions() 0 7 2
A getSubscribedEvents() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like HookManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HookManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Consolidation\AnnotatedCommand\Hooks;
3
4
use Symfony\Component\Console\Command\Command;
5
use Symfony\Component\Console\Input\InputInterface;
6
use Symfony\Component\Console\Output\OutputInterface;
7
8
use Symfony\Component\Console\ConsoleEvents;
9
use Symfony\Component\Console\Event\ConsoleCommandEvent;
10
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11
12
use Consolidation\AnnotatedCommand\ExitCodeInterface;
13
use Consolidation\AnnotatedCommand\OutputDataInterface;
14
use Consolidation\AnnotatedCommand\AnnotationData;
15
16
/**
17
 * Manage named callback hooks
18
 */
19
class HookManager implements EventSubscriberInterface
20
{
21
    protected $hooks = [];
22
    protected $hookOptions = [];
23
24
    const PRE_COMMAND_EVENT = 'pre-command-event';
25
    const COMMAND_EVENT = 'command-event';
26
    const POST_COMMAND_EVENT = 'post-command-event';
27
    const PRE_OPTION_HOOK = 'pre-option';
28
    const OPTION_HOOK = 'option';
29
    const POST_OPTION_HOOK = 'post-option';
30
    const PRE_INITIALIZE = 'pre-initialize';
31
    const INITIALIZE = 'initialize';
32
    const POST_INITIALIZE = 'post-initialize';
33
    const PRE_INTERACT = 'pre-interact';
34
    const INTERACT = 'interact';
35
    const POST_INTERACT = 'post-interact';
36
    const PRE_ARGUMENT_VALIDATOR = 'pre-validate';
37
    const ARGUMENT_VALIDATOR = 'validate';
38
    const POST_ARGUMENT_VALIDATOR = 'post-validate';
39
    const PRE_COMMAND_HOOK = 'pre-command';
40
    const COMMAND_HOOK = 'command';
41
    const POST_COMMAND_HOOK = 'post-command';
42
    const PRE_PROCESS_RESULT = 'pre-process';
43
    const PROCESS_RESULT = 'process';
44
    const POST_PROCESS_RESULT = 'post-process';
45
    const PRE_ALTER_RESULT = 'pre-alter';
46
    const ALTER_RESULT = 'alter';
47
    const POST_ALTER_RESULT = 'post-alter';
48
    const STATUS_DETERMINER = 'status';
49
    const EXTRACT_OUTPUT = 'extract';
50
51
    public function __construct()
52
    {
53
    }
54
55
    public function getAllHooks()
56
    {
57
        return $this->hooks;
58
    }
59
60
    /**
61
     * Add a hook
62
     *
63
     * @param mixed $callback The callback function to call
64
     * @param string   $hook     The name of the hook to add
65
     * @param string   $name     The name of the command to hook
66
     *   ('*' for all)
67
     */
68
    public function add(callable $callback, $hook, $name = '*')
69
    {
70
        if (empty($name)) {
71
            $name = static::getClassNameFromCallback($callback);
72
        }
73
        $this->hooks[$name][$hook][] = $callback;
74
    }
75
76
    public function recordHookOptions($commandInfo, $name)
77
    {
78
        $this->hookOptions[$name][] = $commandInfo;
79
    }
80
81
    public static function getNames($command, $callback)
82
    {
83
        return array_filter(
84
            array_merge(
85
                static::getNamesUsingCommands($command),
86
                [static::getClassNameFromCallback($callback)]
87
            )
88
        );
89
    }
90
91
    protected static function getNamesUsingCommands($command)
92
    {
93
        return array_merge(
94
            [$command->getName()],
95
            $command->getAliases()
96
        );
97
    }
98
99
    /**
100
     * If a command hook does not specify any particular command
101
     * name that it should be attached to, then it will be applied
102
     * to every command that is defined in the same class as the hook.
103
     * This is controlled by using the namespace + class name of
104
     * the implementing class of the callback hook.
105
     */
106
    protected static function getClassNameFromCallback($callback)
107
    {
108
        if (!is_array($callback)) {
109
            return '';
110
        }
111
        $reflectionClass = new \ReflectionClass($callback[0]);
112
        return $reflectionClass->getName();
113
    }
114
115
    /**
116
     * Add an configuration provider hook
117
     *
118
     * @param type InitializeHookInterface $provider
119
     * @param type $name The name of the command to hook
120
     *   ('*' for all)
121
     */
122
    public function addInitializeHook(InitializeHookInterface $initializeHook, $name = '*')
123
    {
124
        $this->hooks[$name][self::INITIALIZE][] = $initializeHook;
125
    }
126
127
    /**
128
     * Add an option hook
129
     *
130
     * @param type ValidatorInterface $validator
131
     * @param type $name The name of the command to hook
132
     *   ('*' for all)
133
     */
134
    public function addOptionHook(OptionHookInterface $interactor, $name = '*')
135
    {
136
        $this->hooks[$name][self::INTERACT][] = $interactor;
137
    }
138
139
    /**
140
     * Add an interact hook
141
     *
142
     * @param type ValidatorInterface $validator
143
     * @param type $name The name of the command to hook
144
     *   ('*' for all)
145
     */
146
    public function addInteractor(InteractorInterface $interactor, $name = '*')
147
    {
148
        $this->hooks[$name][self::INTERACT][] = $interactor;
149
    }
150
151
    /**
152
     * Add a pre-validator hook
153
     *
154
     * @param type ValidatorInterface $validator
155
     * @param type $name The name of the command to hook
156
     *   ('*' for all)
157
     */
158
    public function addPreValidator(ValidatorInterface $validator, $name = '*')
159
    {
160
        $this->hooks[$name][self::PRE_ARGUMENT_VALIDATOR][] = $validator;
161
    }
162
163
    /**
164
     * Add a validator hook
165
     *
166
     * @param type ValidatorInterface $validator
167
     * @param type $name The name of the command to hook
168
     *   ('*' for all)
169
     */
170
    public function addValidator(ValidatorInterface $validator, $name = '*')
171
    {
172
        $this->hooks[$name][self::ARGUMENT_VALIDATOR][] = $validator;
173
    }
174
175
    /**
176
     * Add a pre-command hook.  This is the same as a validator hook, except
177
     * that it will run after all of the post-validator hooks.
178
     *
179
     * @param type ValidatorInterface $preCommand
180
     * @param type $name The name of the command to hook
181
     *   ('*' for all)
182
     */
183
    public function addPreCommandHook(ValidatorInterface $preCommand, $name = '*')
184
    {
185
        $this->hooks[$name][self::PRE_COMMAND_HOOK][] = $preCommand;
186
    }
187
188
    /**
189
     * Add a post-command hook.  This is the same as a pre-process hook,
190
     * except that it will run before the first pre-process hook.
191
     *
192
     * @param type ProcessResultInterface $postCommand
193
     * @param type $name The name of the command to hook
194
     *   ('*' for all)
195
     */
196
    public function addPostCommandHook(ProcessResultInterface $postCommand, $name = '*')
197
    {
198
        $this->hooks[$name][self::POST_COMMAND_HOOK][] = $postCommand;
199
    }
200
201
    /**
202
     * Add a result processor.
203
     *
204
     * @param type ProcessResultInterface $resultProcessor
205
     * @param type $name The name of the command to hook
206
     *   ('*' for all)
207
     */
208
    public function addResultProcessor(ProcessResultInterface $resultProcessor, $name = '*')
209
    {
210
        $this->hooks[$name][self::PROCESS_RESULT][] = $resultProcessor;
211
    }
212
213
    /**
214
     * Add a result alterer. After a result is processed
215
     * by a result processor, an alter hook may be used
216
     * to convert the result from one form to another.
217
     *
218
     * @param type AlterResultInterface $resultAlterer
219
     * @param type $name The name of the command to hook
220
     *   ('*' for all)
221
     */
222
    public function addAlterResult(AlterResultInterface $resultAlterer, $name = '*')
223
    {
224
        $this->hooks[$name][self::ALTER_RESULT][] = $resultAlterer;
225
    }
226
227
    /**
228
     * Add a status determiner. Usually, a command should return
229
     * an integer on error, or a result object on success (which
230
     * implies a status code of zero). If a result contains the
231
     * status code in some other field, then a status determiner
232
     * can be used to call the appropriate accessor method to
233
     * determine the status code.  This is usually not necessary,
234
     * though; a command that fails may return a CommandError
235
     * object, which contains a status code and a result message
236
     * to display.
237
     * @see CommandError::getExitCode()
238
     *
239
     * @param type StatusDeterminerInterface $statusDeterminer
240
     * @param type $name The name of the command to hook
241
     *   ('*' for all)
242
     */
243
    public function addStatusDeterminer(StatusDeterminerInterface $statusDeterminer, $name = '*')
244
    {
245
        $this->hooks[$name][self::STATUS_DETERMINER][] = $statusDeterminer;
246
    }
247
248
    /**
249
     * Add an output extractor. If a command returns an object
250
     * object, by default it is passed directly to the output
251
     * formatter (if in use) for rendering. If the result object
252
     * contains more information than just the data to render, though,
253
     * then an output extractor can be used to call the appopriate
254
     * accessor method of the result object to get the data to
255
     * rendered.  This is usually not necessary, though; it is preferable
256
     * to have complex result objects implement the OutputDataInterface.
257
     * @see OutputDataInterface::getOutputData()
258
     *
259
     * @param type ExtractOutputInterface $outputExtractor
260
     * @param type $name The name of the command to hook
261
     *   ('*' for all)
262
     */
263
    public function addOutputExtractor(ExtractOutputInterface $outputExtractor, $name = '*')
264
    {
265
        $this->hooks[$name][self::EXTRACT_OUTPUT][] = $outputExtractor;
266
    }
267
268
    public function initializeHook(
269
        InputInterface $input,
270
        $names,
271
        AnnotationData $annotationData
272
    ) {
273
        $providers = $this->getInitializeHooks($names, $annotationData);
274
        foreach ($providers as $provider) {
275
            $this->callInjectConfigurationHook($provider, $input, $annotationData);
276
        }
277
    }
278
279
    public function optionsHook(
280
        \Consolidation\AnnotatedCommand\AnnotatedCommand $command,
281
        $names,
282
        AnnotationData $annotationData
283
    ) {
284
        $optionHooks = $this->getOptionHooks($names, $annotationData);
285
        foreach ($optionHooks as $optionHook) {
286
            $this->callOptionHook($optionHook, $command, $annotationData);
287
        }
288
        $commandInfoList = $this->getHookOptionsForCommand($command);
289
        $command->optionsHookForHookAnnotations($commandInfoList);
290
    }
291
292
    protected function getHookOptionsForCommand($command)
293
    {
294
        if (isset($this->hookOptions[$command->getName()])) {
295
            return $this->hookOptions[$command->getName()];
296
        }
297
        return [];
298
    }
299
300
    public function interact(
301
        InputInterface $input,
302
        OutputInterface $output,
303
        $names,
304
        AnnotationData $annotationData
305
    ) {
306
        $interactors = $this->getInteractors($names, $annotationData);
307
        foreach ($interactors as $interactor) {
308
            $this->callInteractor($interactor, $input, $output, $annotationData);
309
        }
310
    }
311
312 View Code Duplication
    public function validateArguments($names, $args, AnnotationData $annotationData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
313
    {
314
        $validators = $this->getValidators($names, $annotationData);
315
        foreach ($validators as $validator) {
316
            $validated = $this->callValidator($validator, $args, $annotationData);
317
            if (is_object($validated)) {
318
                return $validated;
319
            }
320
            if (is_array($validated)) {
321
                $args = $validated;
322
            }
323
        }
324
        return $args;
325
    }
326
327
    /**
328
     * Process result and decide what to do with it.
329
     * Allow client to add transformation / interpretation
330
     * callbacks.
331
     */
332
    public function alterResult($names, $result, $args, AnnotationData $annotationData)
333
    {
334
        $processors = $this->getProcessResultHooks($names, $annotationData);
335
        foreach ($processors as $processor) {
336
            $result = $this->callProcessor($processor, $result, $args, $annotationData);
337
        }
338
        $alterers = $this->getAlterResultHooks($names, $annotationData);
339
        foreach ($alterers as $alterer) {
340
            $result = $this->callProcessor($alterer, $result, $args, $annotationData);
341
        }
342
343
        return $result;
344
    }
345
346
    /**
347
     * Call all status determiners, and see if any of them
348
     * know how to convert to a status code.
349
     */
350
    public function determineStatusCode($names, $result)
351
    {
352
        // If the result (post-processing) is an object that
353
        // implements ExitCodeInterface, then we will ask it
354
        // to give us the status code.
355
        if ($result instanceof ExitCodeInterface) {
356
            return $result->getExitCode();
357
        }
358
359
        // If the result does not implement ExitCodeInterface,
360
        // then we'll see if there is a determiner that can
361
        // extract a status code from the result.
362
        $determiners = $this->getStatusDeterminers($names);
363
        foreach ($determiners as $determiner) {
364
            $status = $this->callDeterminer($determiner, $result);
365
            if (isset($status)) {
366
                return $status;
367
            }
368
        }
369
    }
370
371
    /**
372
     * Convert the result object to printable output in
373
     * structured form.
374
     */
375
    public function extractOutput($names, $result)
376
    {
377
        if ($result instanceof OutputDataInterface) {
378
            return $result->getOutputData();
379
        }
380
381
        $extractors = $this->getOutputExtractors($names);
382
        foreach ($extractors as $extractor) {
383
            $structuredOutput = $this->callExtractor($extractor, $result);
384
            if (isset($structuredOutput)) {
385
                return $structuredOutput;
386
            }
387
        }
388
389
        return $result;
390
    }
391
392
    protected function getCommandEventHooks($names)
393
    {
394
        return $this->getHooks($names, self::COMMAND_EVENT);
395
    }
396
397
    protected function getInitializeHooks($names, AnnotationData $annotationData)
398
    {
399
        return $this->getHooks($names, self::INITIALIZE, $annotationData);
400
    }
401
402
    protected function getOptionHooks($names, AnnotationData $annotationData)
403
    {
404
        return $this->getHooks($names, self::OPTION_HOOK, $annotationData);
405
    }
406
407
    protected function getInteractors($names, AnnotationData $annotationData)
408
    {
409
        return $this->getHooks($names, self::INTERACT, $annotationData);
410
    }
411
412 View Code Duplication
    protected function getValidators($names, AnnotationData $annotationData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
413
    {
414
        return array_merge(
415
            $this->getHooks($names, self::ARGUMENT_VALIDATOR, $annotationData),
416
            $this->getHooks($names, self::COMMAND_HOOK, $annotationData, ['pre-', ''])
417
        );
418
    }
419
420 View Code Duplication
    protected function getProcessResultHooks($names, AnnotationData $annotationData)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
421
    {
422
        return array_merge(
423
            $this->getHooks($names, self::COMMAND_HOOK, $annotationData, ['post-']),
424
            $this->getHooks($names, self::PROCESS_RESULT, $annotationData)
425
        );
426
    }
427
428
    protected function getAlterResultHooks($names, AnnotationData $annotationData)
429
    {
430
        return $this->getHooks($names, self::ALTER_RESULT, $annotationData);
431
    }
432
433
    protected function getStatusDeterminers($names)
434
    {
435
        return $this->getHooks($names, self::STATUS_DETERMINER);
436
    }
437
438
    protected function getOutputExtractors($names)
439
    {
440
        return $this->getHooks($names, self::EXTRACT_OUTPUT);
441
    }
442
443
444
    /**
445
     * Get a set of hooks with the provided name(s). Include the
446
     * pre- and post- hooks, and also include the global hooks ('*')
447
     * in addition to the named hooks provided.
448
     *
449
     * @param string|array $names The name of the function being hooked.
450
     * @param string $hook The specific hook name (e.g. alter)
451
     * @param string[] $stages The stages to apply hooks at (e.g. pre, post)
452
     *
453
     * @return callable[]
454
     */
455
    protected function getHooks($names, $hook, $annotationData = null, $stages = ['pre-', '', 'post-'])
456
    {
457
        $names = array_merge(
458
            (array)$names,
459
            ($annotationData == null) ? [] : array_map(function ($item) {
460
                return "@$item";
461
            }, $annotationData->keys())
462
        );
463
        $names[] = '*';
464
        return $this->get($names, $hook, $stages);
465
    }
466
467
    /**
468
     * Get a set of hooks with the provided name(s).
469
     *
470
     * @param string|array $names The name of the function being hooked.
471
     * @param string $hook The specific hook name (e.g. alter)
472
     *
473
     * @return callable[]
474
     */
475
    public function get($names, $hook, $stages = [''])
476
    {
477
        $hooks = [];
478
        foreach ($stages as $stage) {
479
            foreach ((array)$names as $name) {
480
                $hooks = array_merge($hooks, $this->getHook($name, $stage . $hook));
481
            }
482
        }
483
        return $hooks;
484
    }
485
486
    /**
487
     * Get a single named hook.
488
     *
489
     * @param string $name The name of the hooked method
490
     * @param string $hook The specific hook name (e.g. alter)
491
     *
492
     * @return callable[]
493
     */
494
    protected function getHook($name, $hook)
495
    {
496
        if (isset($this->hooks[$name][$hook])) {
497
            return $this->hooks[$name][$hook];
498
        }
499
        return [];
500
    }
501
502
    protected function callInjectConfigurationHook($provider, $input, AnnotationData $annotationData)
503
    {
504
        if ($provider instanceof InitializeHookInterface) {
505
            return $provider->applyConfiguration($input, $annotationData);
0 ignored issues
show
Bug introduced by
The method applyConfiguration() does not seem to exist on object<Consolidation\Ann...nitializeHookInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
506
        }
507
        if (is_callable($provider)) {
508
            return $provider($input, $annotationData);
509
        }
510
    }
511
512
    protected function callOptionHook($optionHook, $command, AnnotationData $annotationData)
513
    {
514
        if ($optionHook instanceof OptionHookInterface) {
515
            return $optionHook->getOptions($command, $annotationData);
516
        }
517
        if (is_callable($optionHook)) {
518
            return $optionHook($command, $annotationData);
519
        }
520
    }
521
522
    protected function callInteractor($interactor, $input, $output, AnnotationData $annotationData)
523
    {
524
        if ($interactor instanceof InteractorInterface) {
525
            return $interactor->interact($input, $output, $annotationData);
526
        }
527
        if (is_callable($interactor)) {
528
            return $interactor($input, $output, $annotationData);
529
        }
530
    }
531
532
    protected function callValidator($validator, $args, AnnotationData $annotationData)
533
    {
534
        // TODO: Adding AnnotationData to ValidatorInterface would be
535
        // a breaking change. Either hold off until 2.x, or make
536
        // a new interface containing a method that takes the extra parameter.
537
        if ($validator instanceof ValidatorInterface) {
538
            return $validator->validate($args, $annotationData);
539
        }
540
        if (is_callable($validator)) {
541
            return $validator($args, $annotationData);
542
        }
543
    }
544
545
    protected function callProcessor($processor, $result, $args, AnnotationData $annotationData)
546
    {
547
        $processed = null;
548
        // TODO: Adding AnnotationData to ProcessResultInterface would be
549
        // a breaking change. Either hold off until 2.x, or make
550
        // a new interface containing a method that takes the extra parameter.
551
        if ($processor instanceof ProcessResultInterface) {
552
            $processed = $processor->process($result, $args, $annotationData);
553
        }
554
        if (is_callable($processor)) {
555
            $processed = $processor($result, $args, $annotationData);
556
        }
557
        if (isset($processed)) {
558
            return $processed;
559
        }
560
        return $result;
561
    }
562
563
    protected function callDeterminer($determiner, $result)
564
    {
565
        if ($determiner instanceof StatusDeterminerInterface) {
566
            return $determiner->determineStatusCode($result);
567
        }
568
        if (is_callable($determiner)) {
569
            return $determiner($result);
570
        }
571
    }
572
573
    protected function callExtractor($extractor, $result)
574
    {
575
        if ($extractor instanceof ExtractOutputInterface) {
576
            return $extractor->extractOutput($result);
577
        }
578
        if (is_callable($extractor)) {
579
            return $extractor($result);
580
        }
581
    }
582
583
    /**
584
     * @param ConsoleCommandEvent $event
585
     */
586
    public function callCommandEventHooks(ConsoleCommandEvent $event)
587
    {
588
        /* @var Command $command */
589
        $command = $event->getCommand();
590
        $names = [$command->getName()];
591
        $commandEventHooks = $this->getCommandEventHooks($names);
592
        foreach ($commandEventHooks as $commandEvent) {
593
            if (is_callable($commandEvent)) {
594
                $commandEvent($event);
595
            }
596
        }
597
    }
598
599
    public function findAndAddHookOptions($command)
600
    {
601
        if (!$command instanceof \Consolidation\AnnotatedCommand\AnnotatedCommand) {
602
            return;
603
        }
604
        $command->optionsHook();
605
    }
606
607
    /**
608
     * @{@inheritdoc}
609
     */
610
    public static function getSubscribedEvents()
611
    {
612
        return [ConsoleEvents::COMMAND => 'callCommandEventHooks'];
613
    }
614
}
615