Completed
Push — master ( 0df1f8...958d5f )
by Sébastien
07:01
created

Debug::formatSatisfies()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
cc 2
nc 1
nop 1
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
                    return vsprintf('%s: %s', [$key, $value]);
108
                }, $state['metadata'], array_keys($state['metadata'])));
109
            } else {
110
                $metadata = null;
111
            }
112
113
            return [$state['name'], $metadata];
114
        }, $states));
115
    }
116
117
    /**
118
     * Display the graph transitions on a table.
119
     *
120
     * @param array $transitions
121
     */
122
    protected function printTransitions(array $transitions)
123
    {
124
        end($transitions);
125
126
        $lastTransition = key($transitions);
127
128
        reset($transitions);
129
130
        $rows = [];
131
132
        foreach ($transitions as $name => $transition) {
133
            $rows[] = [$name, implode(PHP_EOL, $transition['from']), $transition['to']];
134
135
            if ($name !== $lastTransition) {
136
                $rows[] = new TableSeparator();
137
            }
138
        }
139
140
        $this->table(['Transition', 'From(s)', 'To'], $rows);
141
    }
142
143
    /**
144
     * Display the graph callbacks on a table.
145
     *
146
     * @param array $allCallbacks
147
     */
148
    protected function printCallbacks(array $allCallbacks)
149
    {
150
        foreach ($allCallbacks as $position => $callbacks) {
151
            $rows = [];
152
            foreach ($callbacks as $name => $specs) {
153
                $rows[] = [
154
                    $name,
155
                    $this->formatSatisfies($specs),
156
                    $this->formatCallable($specs),
157
                    $this->formatClause($specs, 'args'),
158
                ];
159
            }
160
161
            $this->table([ucfirst($position).' Callbacks', 'Satisfies', 'Do', 'Args'], $rows);
162
        }
163
    }
164
165
    /**
166
     * Format the clauses that satisfy the callback.
167
     *
168
     * @param  array $specs
169
     * @return string
170
     */
171
    protected function formatSatisfies(array $specs)
172
    {
173
        $clauses = array_map(function ($clause) use ($specs) {
174
            if ($result = $this->formatClause($specs, $clause)) {
175
                return vsprintf('%s: %s', [
176
                    ucfirst(str_replace('_', ' ', $clause)),
177
                    $result,
178
                ]);
179
            }
180
        }, ['from', 'excluded_from', 'on', 'excluded_on', 'to', 'excluded_to']);
181
182
        return implode(PHP_EOL, array_filter($clauses));
183
    }
184
185
    /**
186
     * Format the callback clause.
187
     *
188
     * @param  array  $specs
189
     * @param  string $clause
190
     * @return string
191
     */
192
    protected function formatClause(array $specs, $clause)
193
    {
194
        if (isset($specs[$clause])) {
195
            return implode(', ', (array) $specs[$clause]);
196
        }
197
198
        return '';
199
    }
200
201
    /**
202
     * Format the callable callable.
203
     *
204
     * @param  array  $specs
205
     * @return string
206
     */
207
    protected function formatCallable(array $specs)
208
    {
209
        if (isset($specs['can'])) {
210
            $callback = json_encode($specs['can']);
211
212
            return "Gate::check({$callback})";
213
        }
214
215
        if (! isset($specs['do'])) {
216
            return '';
217
        }
218
219
        $callback = $specs['do'];
220
221
        if ($callback instanceof \Closure) {
222
            return 'Closure';
223
        }
224
225
        if (is_string($callback)) {
226
            if (strpos($callback, '@') !== false) {
227
                $callback = explode('@', $callback);
228
            } else {
229
                return $callback.'()';
230
            }
231
        }
232
233
        if (is_array($callback)) {
234
            return implode('::', $callback).'()';
235
        }
236
237
        return $callback;
238
    }
239
}
240