CommandFactory::generateOutputClassFromApiClass()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Rs\VersionEye\Console;
4
5
use ArgumentsResolver\NamedArgumentsResolver;
6
use Camel\CaseTransformerInterface;
7
use phpDocumentor\Reflection\DocBlockFactory;
8
use Rs\VersionEye\Api\Api;
9
use Rs\VersionEye\Authentication\Token;
10
use Rs\VersionEye\Client;
11
use Symfony\Component\Console\Command\Command;
12
use Symfony\Component\Console\Input\InputArgument;
13
use Symfony\Component\Console\Input\InputDefinition;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
18
/**
19
 * CommandFactory.
20
 *
21
 * @author Robert Schönthal <[email protected]>
22
 */
23
class CommandFactory
24
{
25
    /**
26
     * @var CaseTransformerInterface
27
     */
28
    private $transformer;
29
30
    /**
31
     * @var Token
32
     */
33
    private $token;
34
35 4
    public function __construct(Token $token, CaseTransformerInterface $transformer)
36
    {
37 4
        $this->transformer = $transformer;
38 4
        $this->token       = $token;
39 4
    }
40
41
    /**
42
     * generates Commands from all Api Methods.
43
     *
44
     * @param array $classes
45
     *
46
     * @return Command[]
47
     */
48 3
    public function generateCommands(array $classes = [])
49
    {
50 3
        $classes  = $this->readApis($classes);
51 3
        $token    = $this->token->read();
52 3
        $commands = [];
53
54 3
        foreach ($classes as $class) {
55 3
            $api = new \ReflectionClass($class);
56
57 3
            foreach ($api->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
58 3
                if (0 !== strpos($method->getName(), '__')) { //skip magics
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
59 3
                    $command                       = $this->generateCommand($api->getShortName(), $method, $token);
60 3
                    $commands[$command->getName()] = $command;
61
                }
62
            }
63
        }
64
65 3
        return $commands;
66
    }
67
68
    /**
69
     * creates a Command based on an Api Method.
70
     *
71
     * @param string            $name
72
     * @param \ReflectionMethod $method
73
     * @param string            $token
74
     *
75
     * @return Command
76
     */
77 3
    private function generateCommand($name, \ReflectionMethod $method, $token = null)
78
    {
79 3
        $methodName = $this->transformer->transform($method->getName());
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
80
81 3
        $command  = new Command(strtolower($name . ':' . $methodName));
82 3
        $docBlock = DocBlockFactory::createInstance()->create($method->getDocComment());
83
84 3
        $command->setDefinition($this->buildDefinition($method, $token));
85 3
        $command->setDescription($docBlock->getSummary());
86 3
        $command->setCode($this->createCode($name, $method));
87
88 3
        return $command;
89
    }
90
91
    /**
92
     * builds the Input Definition based upon Api Method Parameters.
93
     *
94
     * @param \ReflectionMethod $method
95
     * @param string            $token
96
     *
97
     * @return InputDefinition
98
     */
99 3
    private function buildDefinition(\ReflectionMethod $method, $token = null)
100
    {
101 3
        $definition = new InputDefinition();
102
103 3
        foreach ($method->getParameters() as $parameter) {
104 3
            if ($parameter->isDefaultValueAvailable()) {
105
                //option
106 3
                $definition->addOption(new InputOption($parameter->getName(), null, InputOption::VALUE_REQUIRED, null, $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null));
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
107
            } else {
108
                //argument
109 3
                $definition->addArgument(new InputArgument($parameter->getName(), InputArgument::REQUIRED, null, null));
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
110
            }
111
        }
112
113 3
        $definition->addOption(new InputOption('token', null, InputOption::VALUE_REQUIRED, 'the auth token to use', $token));
114 3
        $definition->addOption(new InputOption('debug', null, InputOption::VALUE_NONE, 'display raw response'));
115
116 3
        return $definition;
117
    }
118
119
    /**
120
     * creates the command execution code.
121
     *
122
     * @param string            $name
123
     * @param \ReflectionMethod $method
124
     *
125
     * @return \Closure
126
     */
127
    private function createCode($name, \ReflectionMethod $method)
128
    {
129 3
        return function (InputInterface $input, OutputInterface $output) use ($name, $method) {
130 1
            $client = new Client();
131
132 1
            if ($input->getOption('token')) {
133
                $client->authorize($input->getOption('token'));
134
            }
135
136 1
            $methodName  = $method->getName();
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
137 1
            $api         = $client->api(strtolower($name));
138 1
            $args        = (new NamedArgumentsResolver($method))->resolve(array_merge($input->getOptions(), $input->getArguments()));
139 1
            $outputClass = $this->generateOutputClassFromApiClass($api);
140
141 1
            $response = call_user_func_array([$api, $methodName], $args);
142
143 1
            if (method_exists($outputClass, $methodName) && false === $input->getOption('debug')) {
144 1
                (new $outputClass())->{$methodName}($output, $response);
145
            } else {
146
                $output->writeln(print_r($response, true));
147
            }
148 3
        };
149
    }
150
151
    /**
152
     * reads all api classes.
153
     *
154
     * @param array $classes
155
     *
156
     * @return array
157
     */
158 3
    private function readApis(array $classes = [])
159
    {
160
        $baseClasses = [
161 3
            'Rs\VersionEye\Api\Github',
162
            'Rs\VersionEye\Api\Me',
163
            'Rs\VersionEye\Api\Products',
164
            'Rs\VersionEye\Api\Projects',
165
            'Rs\VersionEye\Api\Services',
166
            'Rs\VersionEye\Api\Sessions',
167
            'Rs\VersionEye\Api\Users',
168
        ];
169
170 3
        return array_merge($baseClasses, $classes);
171
    }
172
173
    /**
174
     * remove the last namespace prefix of the Api Class and replace it with Output
175
     * e.g. Rs\VersionEye\Api\Me -> Rs\VersionEye\Output\Me.
176
     *
177
     * @param Api $api
178
     *
179
     * @return string
180
     */
181 1
    private function generateOutputClassFromApiClass(Api $api)
182
    {
183 1
        $classParts = explode('\\', get_class($api));
184 1
        $apiName    = array_pop($classParts);
185 1
        array_pop($classParts);
186
187 1
        return implode('\\', $classParts) . '\\Output\\' . $apiName;
188
    }
189
}
190