Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of Psy Shell.
5
 *
6
 * (c) 2012-2022 Justin Hileman
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Psy\Command;
13
 
14
use Psy\Command\ListCommand\ClassConstantEnumerator;
15
use Psy\Command\ListCommand\ClassEnumerator;
16
use Psy\Command\ListCommand\ConstantEnumerator;
17
use Psy\Command\ListCommand\FunctionEnumerator;
18
use Psy\Command\ListCommand\GlobalVariableEnumerator;
19
use Psy\Command\ListCommand\MethodEnumerator;
20
use Psy\Command\ListCommand\PropertyEnumerator;
21
use Psy\Command\ListCommand\VariableEnumerator;
22
use Psy\Exception\RuntimeException;
23
use Psy\Input\CodeArgument;
24
use Psy\Input\FilterOptions;
25
use Psy\Output\ShellOutput;
26
use Psy\VarDumper\Presenter;
27
use Psy\VarDumper\PresenterAware;
28
use Symfony\Component\Console\Formatter\OutputFormatter;
29
use Symfony\Component\Console\Helper\TableHelper;
30
use Symfony\Component\Console\Input\InputInterface;
31
use Symfony\Component\Console\Input\InputOption;
32
use Symfony\Component\Console\Output\OutputInterface;
33
 
34
/**
35
 * List available local variables, object properties, etc.
36
 */
37
class ListCommand extends ReflectingCommand implements PresenterAware
38
{
39
    protected $presenter;
40
    protected $enumerators;
41
 
42
    /**
43
     * PresenterAware interface.
44
     *
45
     * @param Presenter $presenter
46
     */
47
    public function setPresenter(Presenter $presenter)
48
    {
49
        $this->presenter = $presenter;
50
    }
51
 
52
    /**
53
     * {@inheritdoc}
54
     */
55
    protected function configure()
56
    {
57
        list($grep, $insensitive, $invert) = FilterOptions::getOptions();
58
 
59
        $this
60
            ->setName('ls')
61
            ->setAliases(['dir'])
62
            ->setDefinition([
63
                new CodeArgument('target', CodeArgument::OPTIONAL, 'A target class or object to list.'),
64
 
65
                new InputOption('vars', '', InputOption::VALUE_NONE, 'Display variables.'),
66
                new InputOption('constants', 'c', InputOption::VALUE_NONE, 'Display defined constants.'),
67
                new InputOption('functions', 'f', InputOption::VALUE_NONE, 'Display defined functions.'),
68
                new InputOption('classes', 'k', InputOption::VALUE_NONE, 'Display declared classes.'),
69
                new InputOption('interfaces', 'I', InputOption::VALUE_NONE, 'Display declared interfaces.'),
70
                new InputOption('traits', 't', InputOption::VALUE_NONE, 'Display declared traits.'),
71
 
72
                new InputOption('no-inherit', '', InputOption::VALUE_NONE, 'Exclude inherited methods, properties and constants.'),
73
 
74
                new InputOption('properties', 'p', InputOption::VALUE_NONE, 'Display class or object properties (public properties by default).'),
75
                new InputOption('methods', 'm', InputOption::VALUE_NONE, 'Display class or object methods (public methods by default).'),
76
 
77
                $grep,
78
                $insensitive,
79
                $invert,
80
 
81
                new InputOption('globals', 'g', InputOption::VALUE_NONE, 'Include global variables.'),
82
                new InputOption('internal', 'n', InputOption::VALUE_NONE, 'Limit to internal functions and classes.'),
83
                new InputOption('user', 'u', InputOption::VALUE_NONE, 'Limit to user-defined constants, functions and classes.'),
84
                new InputOption('category', 'C', InputOption::VALUE_REQUIRED, 'Limit to constants in a specific category (e.g. "date").'),
85
 
86
                new InputOption('all', 'a', InputOption::VALUE_NONE, 'Include private and protected methods and properties.'),
87
                new InputOption('long', 'l', InputOption::VALUE_NONE, 'List in long format: includes class names and method signatures.'),
88
            ])
89
            ->setDescription('List local, instance or class variables, methods and constants.')
90
            ->setHelp(
91
                <<<'HELP'
92
List variables, constants, classes, interfaces, traits, functions, methods,
93
and properties.
94
 
95
Called without options, this will return a list of variables currently in scope.
96
 
97
If a target object is provided, list properties, constants and methods of that
98
target. If a class, interface or trait name is passed instead, list constants
99
and methods on that class.
100
 
101
e.g.
102
<return>>>> ls</return>
103
<return>>>> ls $foo</return>
104
<return>>>> ls -k --grep mongo -i</return>
105
<return>>>> ls -al ReflectionClass</return>
106
<return>>>> ls --constants --category date</return>
107
<return>>>> ls -l --functions --grep /^array_.*/</return>
108
<return>>>> ls -l --properties new DateTime()</return>
109
HELP
110
            );
111
    }
112
 
113
    /**
114
     * {@inheritdoc}
115
     *
116
     * @return int 0 if everything went fine, or an exit code
117
     */
118
    protected function execute(InputInterface $input, OutputInterface $output)
119
    {
120
        $this->validateInput($input);
121
        $this->initEnumerators();
122
 
123
        $method = $input->getOption('long') ? 'writeLong' : 'write';
124
 
125
        if ($target = $input->getArgument('target')) {
126
            list($target, $reflector) = $this->getTargetAndReflector($target);
127
        } else {
128
            $reflector = null;
129
        }
130
 
131
        // @todo something cleaner than this :-/
132
        if ($output instanceof ShellOutput && $input->getOption('long')) {
133
            $output->startPaging();
134
        }
135
 
136
        foreach ($this->enumerators as $enumerator) {
137
            $this->$method($output, $enumerator->enumerate($input, $reflector, $target));
138
        }
139
 
140
        if ($output instanceof ShellOutput && $input->getOption('long')) {
141
            $output->stopPaging();
142
        }
143
 
144
        // Set some magic local variables
145
        if ($reflector !== null) {
146
            $this->setCommandScopeVariables($reflector);
147
        }
148
 
149
        return 0;
150
    }
151
 
152
    /**
153
     * Initialize Enumerators.
154
     */
155
    protected function initEnumerators()
156
    {
157
        if (!isset($this->enumerators)) {
158
            $mgr = $this->presenter;
159
 
160
            $this->enumerators = [
161
                new ClassConstantEnumerator($mgr),
162
                new ClassEnumerator($mgr),
163
                new ConstantEnumerator($mgr),
164
                new FunctionEnumerator($mgr),
165
                new GlobalVariableEnumerator($mgr),
166
                new PropertyEnumerator($mgr),
167
                new MethodEnumerator($mgr),
168
                new VariableEnumerator($mgr, $this->context),
169
            ];
170
        }
171
    }
172
 
173
    /**
174
     * Write the list items to $output.
175
     *
176
     * @param OutputInterface $output
177
     * @param array           $result List of enumerated items
178
     */
179
    protected function write(OutputInterface $output, array $result)
180
    {
181
        if (\count($result) === 0) {
182
            return;
183
        }
184
 
185
        foreach ($result as $label => $items) {
186
            $names = \array_map([$this, 'formatItemName'], $items);
187
            $output->writeln(\sprintf('<strong>%s</strong>: %s', $label, \implode(', ', $names)));
188
        }
189
    }
190
 
191
    /**
192
     * Write the list items to $output.
193
     *
194
     * Items are listed one per line, and include the item signature.
195
     *
196
     * @param OutputInterface $output
197
     * @param array           $result List of enumerated items
198
     */
199
    protected function writeLong(OutputInterface $output, array $result)
200
    {
201
        if (\count($result) === 0) {
202
            return;
203
        }
204
 
205
        $table = $this->getTable($output);
206
 
207
        foreach ($result as $label => $items) {
208
            $output->writeln('');
209
            $output->writeln(\sprintf('<strong>%s:</strong>', $label));
210
 
211
            $table->setRows([]);
212
            foreach ($items as $item) {
213
                $table->addRow([$this->formatItemName($item), $item['value']]);
214
            }
215
 
216
            if ($table instanceof TableHelper) {
217
                $table->render($output);
218
            } else {
219
                $table->render();
220
            }
221
        }
222
    }
223
 
224
    /**
225
     * Format an item name given its visibility.
226
     *
227
     * @param array $item
228
     *
229
     * @return string
230
     */
231
    private function formatItemName(array $item): string
232
    {
233
        return \sprintf('<%s>%s</%s>', $item['style'], OutputFormatter::escape($item['name']), $item['style']);
234
    }
235
 
236
    /**
237
     * Validate that input options make sense, provide defaults when called without options.
238
     *
239
     * @throws RuntimeException if options are inconsistent
240
     *
241
     * @param InputInterface $input
242
     */
243
    private function validateInput(InputInterface $input)
244
    {
245
        if (!$input->getArgument('target')) {
246
            // if no target is passed, there can be no properties or methods
247
            foreach (['properties', 'methods', 'no-inherit'] as $option) {
248
                if ($input->getOption($option)) {
249
                    throw new RuntimeException('--'.$option.' does not make sense without a specified target');
250
                }
251
            }
252
 
253
            foreach (['globals', 'vars', 'constants', 'functions', 'classes', 'interfaces', 'traits'] as $option) {
254
                if ($input->getOption($option)) {
255
                    return;
256
                }
257
            }
258
 
259
            // default to --vars if no other options are passed
260
            $input->setOption('vars', true);
261
        } else {
262
            // if a target is passed, classes, functions, etc don't make sense
263
            foreach (['vars', 'globals'] as $option) {
264
                if ($input->getOption($option)) {
265
                    throw new RuntimeException('--'.$option.' does not make sense with a specified target');
266
                }
267
            }
268
 
269
            // @todo ensure that 'functions', 'classes', 'interfaces', 'traits' only accept namespace target?
270
            foreach (['constants', 'properties', 'methods', 'functions', 'classes', 'interfaces', 'traits'] as $option) {
271
                if ($input->getOption($option)) {
272
                    return;
273
                }
274
            }
275
 
276
            // default to --constants --properties --methods if no other options are passed
277
            $input->setOption('constants', true);
278
            $input->setOption('properties', true);
279
            $input->setOption('methods', true);
280
        }
281
    }
282
}