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\Formatter\DocblockFormatter;
15
use Psy\Formatter\SignatureFormatter;
16
use Psy\Input\CodeArgument;
17
use Psy\Output\ShellOutput;
18
use Psy\Reflection\ReflectionClassConstant;
19
use Psy\Reflection\ReflectionConstant_;
20
use Psy\Reflection\ReflectionLanguageConstruct;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
 
25
/**
26
 * Read the documentation for an object, class, constant, method or property.
27
 */
28
class DocCommand extends ReflectingCommand
29
{
30
    const INHERIT_DOC_TAG = '{@inheritdoc}';
31
 
32
    /**
33
     * {@inheritdoc}
34
     */
35
    protected function configure()
36
    {
37
        $this
38
            ->setName('doc')
39
            ->setAliases(['rtfm', 'man'])
40
            ->setDefinition([
41
                new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show documentation for superclasses as well as the current class.'),
42
                new CodeArgument('target', CodeArgument::REQUIRED, 'Function, class, instance, constant, method or property to document.'),
43
            ])
44
            ->setDescription('Read the documentation for an object, class, constant, method or property.')
45
            ->setHelp(
46
                <<<HELP
47
Read the documentation for an object, class, constant, method or property.
48
 
49
It's awesome for well-documented code, not quite as awesome for poorly documented code.
50
 
51
e.g.
52
<return>>>> doc preg_replace</return>
53
<return>>>> doc Psy\Shell</return>
54
<return>>>> doc Psy\Shell::debug</return>
55
<return>>>> \$s = new Psy\Shell</return>
56
<return>>>> doc \$s->run</return>
57
HELP
58
            );
59
    }
60
 
61
    /**
62
     * {@inheritdoc}
63
     *
64
     * @return int 0 if everything went fine, or an exit code
65
     */
66
    protected function execute(InputInterface $input, OutputInterface $output)
67
    {
68
        $value = $input->getArgument('target');
69
        if (ReflectionLanguageConstruct::isLanguageConstruct($value)) {
70
            $reflector = new ReflectionLanguageConstruct($value);
71
            $doc = $this->getManualDocById($value);
72
        } else {
73
            list($target, $reflector) = $this->getTargetAndReflector($value);
74
            $doc = $this->getManualDoc($reflector) ?: DocblockFormatter::format($reflector);
75
        }
76
 
77
        $db = $this->getApplication()->getManualDb();
78
 
79
        if ($output instanceof ShellOutput) {
80
            $output->startPaging();
81
        }
82
 
83
        // Maybe include the declaring class
84
        if ($reflector instanceof \ReflectionMethod || $reflector instanceof \ReflectionProperty) {
85
            $output->writeln(SignatureFormatter::format($reflector->getDeclaringClass()));
86
        }
87
 
88
        $output->writeln(SignatureFormatter::format($reflector));
89
        $output->writeln('');
90
 
91
        if (empty($doc) && !$db) {
92
            $output->writeln('<warning>PHP manual not found</warning>');
93
            $output->writeln('    To document core PHP functionality, download the PHP reference manual:');
94
            $output->writeln('    https://github.com/bobthecow/psysh/wiki/PHP-manual');
95
        } else {
96
            $output->writeln($doc);
97
        }
98
 
99
        // Implicit --all if the original docblock has an {@inheritdoc} tag.
100
        if ($input->getOption('all') || \stripos($doc, self::INHERIT_DOC_TAG) !== false) {
101
            $parent = $reflector;
102
            foreach ($this->getParentReflectors($reflector) as $parent) {
103
                $output->writeln('');
104
                $output->writeln('---');
105
                $output->writeln('');
106
 
107
                // Maybe include the declaring class
108
                if ($parent instanceof \ReflectionMethod || $parent instanceof \ReflectionProperty) {
109
                    $output->writeln(SignatureFormatter::format($parent->getDeclaringClass()));
110
                }
111
 
112
                $output->writeln(SignatureFormatter::format($parent));
113
                $output->writeln('');
114
 
115
                if ($doc = $this->getManualDoc($parent) ?: DocblockFormatter::format($parent)) {
116
                    $output->writeln($doc);
117
                }
118
            }
119
        }
120
 
121
        if ($output instanceof ShellOutput) {
122
            $output->stopPaging();
123
        }
124
 
125
        // Set some magic local variables
126
        $this->setCommandScopeVariables($reflector);
127
 
128
        return 0;
129
    }
130
 
131
    private function getManualDoc($reflector)
132
    {
133
        switch (\get_class($reflector)) {
134
            case \ReflectionClass::class:
135
            case \ReflectionObject::class:
136
            case \ReflectionFunction::class:
137
                $id = $reflector->name;
138
                break;
139
 
140
            case \ReflectionMethod::class:
141
                $id = $reflector->class.'::'.$reflector->name;
142
                break;
143
 
144
            case \ReflectionProperty::class:
145
                $id = $reflector->class.'::$'.$reflector->name;
146
                break;
147
 
148
            case \ReflectionClassConstant::class:
149
            case ReflectionClassConstant::class:
150
                // @todo this is going to collide with ReflectionMethod ids
151
                // someday... start running the query by id + type if the DB
152
                // supports it.
153
                $id = $reflector->class.'::'.$reflector->name;
154
                break;
155
 
156
            case ReflectionConstant_::class:
157
                $id = $reflector->name;
158
                break;
159
 
160
            default:
161
                return false;
162
        }
163
 
164
        return $this->getManualDocById($id);
165
    }
166
 
167
    /**
168
     * Get all all parent Reflectors for a given Reflector.
169
     *
170
     * For example, passing a Class, Object or TraitReflector will yield all
171
     * traits and parent classes. Passing a Method or PropertyReflector will
172
     * yield Reflectors for the same-named method or property on all traits and
173
     * parent classes.
174
     *
175
     * @return \Generator a whole bunch of \Reflector instances
176
     */
177
    private function getParentReflectors($reflector): \Generator
178
    {
179
        $seenClasses = [];
180
 
181
        switch (\get_class($reflector)) {
182
            case \ReflectionClass::class:
183
            case \ReflectionObject::class:
184
                foreach ($reflector->getTraits() as $trait) {
185
                    if (!\in_array($trait->getName(), $seenClasses)) {
186
                        $seenClasses[] = $trait->getName();
187
                        yield $trait;
188
                    }
189
                }
190
 
191
                foreach ($reflector->getInterfaces() as $interface) {
192
                    if (!\in_array($interface->getName(), $seenClasses)) {
193
                        $seenClasses[] = $interface->getName();
194
                        yield $interface;
195
                    }
196
                }
197
 
198
                while ($reflector = $reflector->getParentClass()) {
199
                    yield $reflector;
200
 
201
                    foreach ($reflector->getTraits() as $trait) {
202
                        if (!\in_array($trait->getName(), $seenClasses)) {
203
                            $seenClasses[] = $trait->getName();
204
                            yield $trait;
205
                        }
206
                    }
207
 
208
                    foreach ($reflector->getInterfaces() as $interface) {
209
                        if (!\in_array($interface->getName(), $seenClasses)) {
210
                            $seenClasses[] = $interface->getName();
211
                            yield $interface;
212
                        }
213
                    }
214
                }
215
 
216
                return;
217
 
218
            case \ReflectionMethod::class:
219
                foreach ($this->getParentReflectors($reflector->getDeclaringClass()) as $parent) {
220
                    if ($parent->hasMethod($reflector->getName())) {
221
                        $parentMethod = $parent->getMethod($reflector->getName());
222
                        if (!\in_array($parentMethod->getDeclaringClass()->getName(), $seenClasses)) {
223
                            $seenClasses[] = $parentMethod->getDeclaringClass()->getName();
224
                            yield $parentMethod;
225
                        }
226
                    }
227
                }
228
 
229
                return;
230
 
231
            case \ReflectionProperty::class:
232
                foreach ($this->getParentReflectors($reflector->getDeclaringClass()) as $parent) {
233
                    if ($parent->hasProperty($reflector->getName())) {
234
                        $parentProperty = $parent->getProperty($reflector->getName());
235
                        if (!\in_array($parentProperty->getDeclaringClass()->getName(), $seenClasses)) {
236
                            $seenClasses[] = $parentProperty->getDeclaringClass()->getName();
237
                            yield $parentProperty;
238
                        }
239
                    }
240
                }
241
                break;
242
        }
243
    }
244
 
245
    private function getManualDocById($id)
246
    {
247
        if ($db = $this->getApplication()->getManualDb()) {
248
            $result = $db->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)));
249
            if ($result !== false) {
250
                return $result->fetchColumn(0);
251
            }
252
        }
253
    }
254
}