Passed
Push — master ( 65ca3f...66b084 )
by Radosław
02:04
created

CreateDocsCommand   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 135
rs 10
c 0
b 0
f 0
wmc 12
1
<?php
2
declare(strict_types=1);
3
4
namespace Phln\Build\Command;
5
6
use Illuminate\Console\Command;
7
use Illuminate\Filesystem\Filesystem;
8
use Illuminate\View\Factory;
9
use phln\Phln;
10
use phpDocumentor\Reflection\DocBlock;
11
use phpDocumentor\Reflection\DocBlock\Tags\BaseTag;
12
use phpDocumentor\Reflection\DocBlockFactory;
13
use function phln\collection\filter;
14
use function phln\collection\map;
15
use function phln\collection\reduce;
16
use function phln\fn\apply;
17
use function phln\fn\pipe;
18
use function phln\object\pathOr;
19
20
class CreateDocsCommand extends Command
21
{
22
    protected $signature = 'create:docs';
23
24
    private $docBlockFactory;
25
26
    private $paths = [
27
        'functions' => __DIR__.'/../../docs/functions/',
28
    ];
29
30
    /**
31
     * @var Factory
32
     */
33
    private $view;
34
35
    /**
36
     * @var Filesystem
37
     */
38
    private $filesystem;
39
40
    public function __construct(Factory $view, Filesystem $filesystem)
41
    {
42
        parent::__construct();
43
        $this->docBlockFactory = DocBlockFactory::createInstance();
44
        $this->view = $view;
45
        $this->filesystem = $filesystem;
46
    }
47
48
    public function handle()
49
    {
50
        $this->output->writeln('Preparing directories');
51
        $this->cleanup();
52
53
        $this->output->writeln('Extracting contents');
54
        $phlnReflection = new \ReflectionClass(Phln::class);
55
        $docBlocks = $this->extractDocBlocks($phlnReflection);
56
57
        foreach ($docBlocks as $category => $methods) {
58
            $this->output->writeln("Exporting docs for {$category}");
59
            $this->exportCategoryDocs($category, $methods);
60
        }
61
62
        $this->output->writeln('<info>Done</info>');
63
    }
64
65
    private function cleanup()
66
    {
67
        foreach ($this->paths as $path) {
68
69
            if ($this->filesystem->isDirectory($path)) {
70
                $this->filesystem->cleanDirectory($path);
71
            } else {
72
                $this->filesystem->delete($this->paths);
73
            }
74
        }
75
    }
76
77
    /**
78
     * Extract doc blocks of methods and group them by `@phlnCategory` tag
79
     *
80
     * @param \ReflectionClass $reflection
81
     * @return array
82
     */
83
    private function extractDocBlocks(\ReflectionClass $reflection): array
84
    {
85
        $methods = $reflection->getMethods();
86
87
        return apply(
88
            pipe([
89
                map(function (\ReflectionMethod $method) {
90
                    $comment = $method->getDocComment();
91
92
                    return [
93
                        'name' => $method->getName(),
94
                        'docBlock' => $this->docBlockFactory->create($comment),
95
                    ];
96
                }),
97
                reduce(function ($carry, array $method) {
98
                    $category = (string)$this->getDocBlockTag('phlnCategory', $method['docBlock']->getTags());
99
100
                    if (true === empty($category)) {
101
                        throw new \RuntimeException("Missing category for function: {$method['name']}");
102
                    }
103
104
                    return array_merge($carry, [
105
                        $category => array_merge(pathOr($category, [], $carry), [$this->getMethodToView($method)]),
106
                    ]);
107
                }, [])
108
            ]),
109
            [$methods]
110
        );
111
    }
112
113
    private function getDocBlockTag(string $name, array $tags)
114
    {
115
        return \phln\collection\head($this->getDocBlockTagsList($name, $tags));
116
    }
117
118
    /**
119
     * @param string $name
120
     * @param BaseTag[] $tags
121
     * @return array
122
     */
123
    private function getDocBlockTagsList(string $name, array $tags): array
124
    {
125
        return filter(function (BaseTag $tag) use ($name) {
126
            return $name === $tag->getName();
127
        }, $tags);
128
    }
129
130
    /**
131
     * @param string $category
132
     * @param array $methods
133
     */
134
    private function exportCategoryDocs(string $category, array $methods)
135
    {
136
        $rendered = $this->view->make('docs.category')
137
            ->with(compact('category', 'methods'))
138
            ->render();
139
140
        $this->filesystem->append($this->paths['functions']."/{$category}.md", $rendered);
141
    }
142
143
    public function getMethodToView(array $method): array
144
    {
145
        /** @var DocBlock $docBlock */
146
        $docBlock = $method['docBlock'];
147
        $tags = $docBlock->getTags();
148
        $name = $method['name'];
149
        $signatures = $this->getDocBlockTagsList('phlnSignature', $tags);
150
        $example = $this->getDocBlockTag('example', $tags);
151
        $summary = $docBlock->getSummary();
152
        $description = $docBlock->getDescription();
153
154
        return compact('name', 'signatures', 'example', 'summary', 'description');
155
    }
156
}
157