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

AnnotatedCommandFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
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, $commandInfo->getAnnotations());
0 ignored issues
show
Unused Code introduced by
The call to AnnotatedCommand::setCommandProcessor() has too many arguments starting with $commandInfo->getAnnotations().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
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