Completed
Pull Request — master (#33)
by
unknown
01:14
created

Debug   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 3
dl 0
loc 236
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A handle() 0 31 5
A askForGraph() 0 14 1
B printStates() 0 22 6
A printTransitions() 0 20 3
A printCallbacks() 0 16 3
A formatSatisfies() 0 13 2
A formatClause() 0 8 2
B formatCallable() 0 32 7
1
<?php
2
3
namespace Sebdesign\SM\Commands;
4
5
use Illuminate\Console\Command;
6
use Symfony\Component\Console\Helper\TableSeparator;
7
8
class Debug extends Command
9
{
10
    /**
11
     * The name and signature of the console command.
12
     *
13
     * @var string
14
     */
15
    protected $signature = 'winzou:state-machine:debug {graph? : A state machine graph}';
16
17
    /**
18
     * The console command description.
19
     *
20
     * @var string
21
     */
22
    protected $description = 'Show states and transitions of state machine graphs';
23
24
    protected $config;
25
26
    /**
27
     * Create a new command instance.
28
     *
29
     * @param array $config
30
     */
31
    public function __construct(array $config)
32
    {
33
        parent::__construct();
34
35
        $this->config = $config;
36
    }
37
38
    /**
39
     * Execute the console command.
40
     *
41
     * @return mixed
42
     */
43
    public function handle()
44
    {
45
        if (empty($this->config)) {
46
            $this->error('There are no state machines configured.');
47
48
            return 1;
49
        }
50
51
        if (! $this->argument('graph')) {
52
            $this->askForGraph();
53
        }
54
55
        $graph = $this->argument('graph');
56
57
        if (! array_key_exists($graph, $this->config)) {
58
            $this->error('The provided state machine graph is not configured.');
59
60
            return 1;
61
        }
62
63
        $config = $this->config[$graph];
64
65
        $this->printStates($config['states']);
66
        $this->printTransitions($config['transitions']);
67
68
        if (isset($config['callbacks'])) {
69
            $this->printCallbacks($config['callbacks']);
70
        }
71
72
        return 0;
73
    }
74
75
    /**
76
     * Ask for a graph name if one was not provided as argument.
77
     */
78
    protected function askForGraph()
79
    {
80
        $choices = array_map(function ($name, $config) {
81
            return $name."\t(".$config['class'].' - '.$config['graph'].')';
82
        }, array_keys($this->config), $this->config);
83
84
        $choice = $this->choice('Which state machine would you like to know about?', $choices, 0);
85
86
        $choice = substr($choice, 0, strpos($choice, "\t"));
87
88
        $this->info('You have just selected: '.$choice);
89
90
        $this->input->setArgument('graph', $choice);
91
    }
92
93
    /**
94
     * Display the graph states on a table.
95
     *
96
     * @param array $states
97
     */
98
    protected function printStates(array $states)
99
    {
100
        $this->table(['Configured States:', 'Metadata:'], array_map(function ($state) {
101
            if (is_string($state)) {
102
                return [$state, null];
103
            }
104
105
            if (isset($state['metadata'])) {
106
                $metadata = implode(PHP_EOL, array_map(function ($value, $key) {
107
                    $value = is_array($value) ? 'Array' : $value;
108
                    if (is_bool($value)) {
109
                        $value = $value ? 'true' : 'false';
110
                    }
111
                    return vsprintf('%s: %s', [$key, $value]);
112
                }, $state['metadata'], array_keys($state['metadata'])));
113
            } else {
114
                $metadata = null;
115
            }
116
117
            return [$state['name'], $metadata];
118
        }, $states));
119
    }
120
121
    /**
122
     * Display the graph transitions on a table.
123
     *
124
     * @param array $transitions
125
     */
126
    protected function printTransitions(array $transitions)
127
    {
128
        end($transitions);
129
130
        $lastTransition = key($transitions);
131
132
        reset($transitions);
133
134
        $rows = [];
135
136
        foreach ($transitions as $name => $transition) {
137
            $rows[] = [$name, implode(PHP_EOL, $transition['from']), $transition['to']];
138
139
            if ($name !== $lastTransition) {
140
                $rows[] = new TableSeparator();
141
            }
142
        }
143
144
        $this->table(['Transition', 'From(s)', 'To'], $rows);
145
    }
146
147
    /**
148
     * Display the graph callbacks on a table.
149
     *
150
     * @param array $allCallbacks
151
     */
152
    protected function printCallbacks(array $allCallbacks)
153
    {
154
        foreach ($allCallbacks as $position => $callbacks) {
155
            $rows = [];
156
            foreach ($callbacks as $name => $specs) {
157
                $rows[] = [
158
                    $name,
159
                    $this->formatSatisfies($specs),
160
                    $this->formatCallable($specs),
161
                    $this->formatClause($specs, 'args'),
162
                ];
163
            }
164
165
            $this->table([ucfirst($position).' Callbacks', 'Satisfies', 'Do', 'Args'], $rows);
166
        }
167
    }
168
169
    /**
170
     * Format the clauses that satisfy the callback.
171
     *
172
     * @param  array $specs
173
     * @return string
174
     */
175
    protected function formatSatisfies(array $specs)
176
    {
177
        $clauses = array_map(function ($clause) use ($specs) {
178
            if ($result = $this->formatClause($specs, $clause)) {
179
                return vsprintf('%s: %s', [
180
                    ucfirst(str_replace('_', ' ', $clause)),
181
                    $result,
182
                ]);
183
            }
184
        }, ['from', 'excluded_from', 'on', 'excluded_on', 'to', 'excluded_to']);
185
186
        return implode(PHP_EOL, array_filter($clauses));
187
    }
188
189
    /**
190
     * Format the callback clause.
191
     *
192
     * @param  array  $specs
193
     * @param  string $clause
194
     * @return string
195
     */
196
    protected function formatClause(array $specs, $clause)
197
    {
198
        if (isset($specs[$clause])) {
199
            return implode(', ', (array) $specs[$clause]);
200
        }
201
202
        return '';
203
    }
204
205
    /**
206
     * Format the callable callable.
207
     *
208
     * @param  array  $specs
209
     * @return string
210
     */
211
    protected function formatCallable(array $specs)
212
    {
213
        if (isset($specs['can'])) {
214
            $callback = json_encode($specs['can']);
215
216
            return "Gate::check({$callback})";
217
        }
218
219
        if (! isset($specs['do'])) {
220
            return '';
221
        }
222
223
        $callback = $specs['do'];
224
225
        if ($callback instanceof \Closure) {
226
            return 'Closure';
227
        }
228
229
        if (is_string($callback)) {
230
            if (strpos($callback, '@') !== false) {
231
                $callback = explode('@', $callback);
232
            } else {
233
                return $callback.'()';
234
            }
235
        }
236
237
        if (is_array($callback)) {
238
            return implode('::', $callback).'()';
239
        }
240
241
        return $callback;
242
    }
243
}
244