Completed
Push — master ( f0da9f...338f66 )
by Greg
03:53 queued 01:36
created

createCommandsFromClassInfo()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 13
c 1
b 0
f 0
rs 9.4285
cc 3
eloc 7
nc 3
nop 2
1
<?php
2
namespace Consolidation\AnnotatedCommand;
3
4
use Symfony\Component\Console\Command\Command;
5
use Symfony\Component\Console\Input\InputInterface;
6
use Symfony\Component\Console\Output\OutputInterface;
7
8
class AnnotatedCommandFactory
9
{
10
    protected $commandProcessor;
11
    protected $listeners = [];
12
13
    public function __construct()
14
    {
15
        $this->commandProcessor = new CommandProcessor(new HookManager());
16
    }
17
18
    public function setCommandProcessor($commandProcessor)
19
    {
20
        $this->commandProcessor = $commandProcessor;
21
    }
22
23
    public function commandProcessor()
24
    {
25
        return $this->commandProcessor;
26
    }
27
28
    public function addListener($listener)
29
    {
30
        $this->listeners[] = $listener;
31
    }
32
33
    protected function notify($commandFileInstance)
34
    {
35
        foreach ($this->listeners as $listener) {
36
            if ($listener instanceof CommandCreationListenerInterface) {
37
                $listener->notifyCommandFileAdded($commandFileInstance);
38
            }
39
            if (is_callable($listener)) {
40
                $listener($commandFileInstance);
41
            }
42
        }
43
    }
44
45
    public function createCommandsFromClass($commandFileInstance)
46
    {
47
        $this->notify($commandFileInstance);
48
        $commandInfoList = $this->getCommandInfoListFromClass($commandFileInstance);
49
        return $this->createCommandsFromClassInfo($commandInfoList, $commandFileInstance);
50
    }
51
52
    public function getCommandInfoListFromClass($classNameOrInstance)
53
    {
54
        $commandInfoList = [];
55
56
        // Ignore special functions, such as __construct and __call, and
57
        // accessor methods such as getFoo and setFoo, while allowing
58
        // set or setup.
59
        $commandMethodNames = array_filter(
60
            get_class_methods($classNameOrInstance) ?: [],
61
            function ($m) {
62
                return !preg_match('#^(_|get[A-Z]|set[A-Z])#', $m);
63
            }
64
        );
65
66
        foreach ($commandMethodNames as $commandMethodName) {
67
            $commandInfoList[] = new CommandInfo($classNameOrInstance, $commandMethodName);
68
        }
69
70
        return $commandInfoList;
71
    }
72
73
    public function createCommandInfo($classNameOrInstance, $commandMethodName)
74
    {
75
        return new CommandInfo($classNameOrInstance, $commandMethodName);
76
    }
77
78
    public function createCommandsFromClassInfo($commandInfoList, $commandFileInstance)
79
    {
80
        $commandList = [];
81
82
        foreach ($commandInfoList as $commandInfo) {
83
            if (!$commandInfo->hasAnnotation('hook')) {
84
                $command = $this->createCommand($commandInfo, $commandFileInstance);
85
                $commandList[] = $command;
86
            }
87
        }
88
89
        return $commandList;
90
    }
91
92
    public function registerCommandHooksFromClassInfo($commandInfoList, $commandFileInstance)
93
    {
94
        foreach ($commandInfoList as $commandInfo) {
95
            if ($commandInfo->hasAnnotation('hook')) {
96
                $this->registerCommandHook($commandInfo, $commandFileInstance);
97
            }
98
        }
99
    }
100
101
    /**
102
     * Register a command hook given the CommandInfo for a method.
103
     *
104
     * The hook format is:
105
     *
106
     *   @hook type name type
107
     *
108
     * For example, the pre-validate hook for the core-init command is:
109
     *
110
     *   @hook pre-validate core-init
111
     *
112
     * If no command name is provided, then we will presume
113
     * that the name of this method is the same as the name
114
     * of the command being hooked (in a different commandFile).
115
     *
116
     * If no hook is provided, then we will presume that ALTER_RESULT
117
     * is intended.
118
     *
119
     * @param CommandInfo $commandInfo Information about the command hook method.
120
     * @param object $commandFileInstance An instance of the CommandFile class.
121
     */
122
    public function registerCommandHook(CommandInfo $commandInfo, $commandFileInstance)
123
    {
124
        // Ignore if the command info has no @hook
125
        if (!$commandInfo->hasAnnotation('hook')) {
126
            return;
127
        }
128
        $hookData = $commandInfo->getAnnotation('hook');
129
        $hook = $this->getNthWord($hookData, 0, HookManager::ALTER_RESULT);
130
        $commandName = $this->getNthWord($hookData, 1, $commandInfo->getName());
131
132
        // Register the hook
133
        $callback = [$commandFileInstance, $commandInfo->getMethodName()];
134
        $this->commandProcessor()->hookManager()->add($commandName, $hook, $callback);
135
    }
136
137
    protected function getNthWord($string, $n, $default, $delimiter = ' ')
138
    {
139
        $words = explode($delimiter, $string);
140
        if (!empty($words[$n])) {
141
            return $words[$n];
142
        }
143
        return $default;
144
    }
145
146
    public function createCommand(CommandInfo $commandInfo, $commandFileInstance)
147
    {
148
        $command = new AnnotatedCommand($commandInfo->getName());
149
        $commandCallback = [$commandFileInstance, $commandInfo->getMethodName()];
150
        $command->setCommandCallback($commandCallback);
151
        $command->setCommandProcessor($this->commandProcessor);
152
        $command->setCommandInfo($commandInfo);
153
        // Annotation commands are never bootstrap-aware, but for completeness
154
        // we will notify on every created command, as some clients may wish to
155
        // use this notification for some other purpose.
156
        $this->notify($command);
157
        return $command;
158
    }
159
}
160