• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JBZoo / Composer-Graph / 5399088069

pending completion
5399088069

push

github

web-flow
New code style and PHP 8.1+ (#14)

86 of 86 new or added lines in 5 files covered. (100.0%)

276 of 310 relevant lines covered (89.03%)

76.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.39
/src/Commands/Build.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - Composer-Graph.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/Composer-Graph
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\ComposerGraph\Commands;
18

19
use JBZoo\Cli\CliCommand;
20
use JBZoo\Cli\Codes;
21
use JBZoo\Cli\OutLvl;
22
use JBZoo\ComposerGraph\Collection;
23
use JBZoo\ComposerGraph\ComposerGraph;
24
use JBZoo\Data\JSON;
25
use JBZoo\MermaidPHP\Graph;
26
use Symfony\Component\Console\Input\InputOption;
27

28
use function JBZoo\Data\json;
29

30
class Build extends CliCommand
31
{
32
    /**
33
     * {@inheritDoc}
34
     */
35
    protected function configure(): void
36
    {
37
        $required = InputOption::VALUE_REQUIRED;
96✔
38
        $none     = InputOption::VALUE_NONE;
96✔
39

40
        if (!\defined('\IS_PHPUNIT_TEST')) {
96✔
41
            \define('\IS_PHPUNIT_TEST', false);
×
42
        }
43

44
        $this
48✔
45
            ->setName('build')
96✔
46
            ->addOption(
96✔
47
                'root',
48✔
48
                'r',
48✔
49
                $required,
48✔
50
                'The path has to contain ' .
48✔
51
                '"composer.json" and "composer.lock" files',
48✔
52
                './',
48✔
53
            )
48✔
54
            ->addOption('output', 'o', $required, 'Path to html output.', './build/composer-graph.html')
96✔
55
            ->addOption(
96✔
56
                'format',
48✔
57
                'f',
48✔
58
                $required,
48✔
59
                'Output format. Available options: <info>' . \implode(',', [
96✔
60
                    ComposerGraph::FORMAT_HTML,
48✔
61
                    ComposerGraph::FORMAT_MERMAID,
48✔
62
                ]) . '</info>',
48✔
63
                ComposerGraph::FORMAT_HTML,
48✔
64
            )
48✔
65
            ->addOption(
96✔
66
                'direction',
48✔
67
                'D',
48✔
68
                $required,
48✔
69
                'Direction of graph. Available options: <info>' . \implode(',', [
96✔
70
                    Graph::LEFT_RIGHT,
48✔
71
                    Graph::TOP_BOTTOM,
48✔
72
                    Graph::BOTTOM_TOP,
48✔
73
                    Graph::RIGHT_LEFT,
48✔
74
                ]) . '</info>',
48✔
75
                Graph::LEFT_RIGHT,
48✔
76
            )
48✔
77
            ->addOption('show-php', 'p', $none, 'Show PHP-node')
96✔
78
            ->addOption('show-ext', 'e', $none, 'Show all ext-* nodes (PHP modules)')
96✔
79
            ->addOption('show-dev', 'd', $none, 'Show all dev dependencies')
96✔
80
            ->addOption('show-suggests', 's', $none, 'Show not installed suggests packages')
96✔
81
            ->addOption('show-link-versions', 'l', $none, 'Show version requirements in links')
96✔
82
            ->addOption('show-package-versions', 'P', $none, 'Show version of packages')
96✔
83
            ->addOption(
96✔
84
                'abc-order',
48✔
85
                'O',
48✔
86
                $none,
48✔
87
                'Strict ABC ordering nodes in graph. ' .
48✔
88
                "It's fine tuning, sometimes it useful.",
48✔
89
            );
48✔
90

91
        parent::configure();
96✔
92
    }
93

94
    /**
95
     * {@inheritDoc}
96
     */
97
    protected function executeAction(): int
98
    {
99
        $format = $this->getOptString('format');
96✔
100

101
        [$composerJson, $composerLock] = $this->getJsonData();
96✔
102
        $vendorDir                     = $this->findVendorDir($composerJson);
96✔
103

104
        $composerGraph = new ComposerGraph(
96✔
105
            new Collection($composerJson, $composerLock, $vendorDir),
96✔
106
            [
48✔
107
                'direction'    => $this->getOptString('direction'),
96✔
108
                'php'          => $this->getOptBool('show-php'),
96✔
109
                'ext'          => $this->getOptBool('show-ext'),
96✔
110
                'dev'          => $this->getOptBool('show-dev'),
96✔
111
                'suggest'      => $this->getOptBool('show-suggests'),
96✔
112
                'link-version' => $this->getOptBool('show-link-versions'),
96✔
113
                'lib-version'  => $this->getOptBool('show-package-versions'),
96✔
114
                'format'       => $format,
48✔
115
                'output-path'  => $this->getOptString('output'),
96✔
116
                'abc-order'    => $this->getOptBool('abc-order'),
96✔
117
            ],
48✔
118
        );
48✔
119

120
        $result = $composerGraph->build();
96✔
121
        if ($format === ComposerGraph::FORMAT_HTML) {
96✔
122
            $this->_("Report is ready: <info>{$result}</info>");
96✔
123
        } else {
124
            $this->_($result);
96✔
125
        }
126

127
        return Codes::OK;
96✔
128
    }
129

130
    private function getRootPath(): string
131
    {
132
        $origRootPath = $this->getOptString('root');
96✔
133
        $realRootPath = \realpath($origRootPath);
96✔
134

135
        // Validate root path
136
        if ($realRootPath === false || !\is_dir($realRootPath)) {
96✔
137
            throw new Exception("Root path is not directory or not found: {$origRootPath}");
×
138
        }
139

140
        return $realRootPath;
96✔
141
    }
142

143
    /**
144
     * @return JSON[]
145
     */
146
    private function getJsonData(): array
147
    {
148
        $realRootPath = $this->getRootPath();
96✔
149

150
        // Validate "composer.json" path and file
151
        $composerJsonPath = "{$realRootPath}/composer.json";
96✔
152
        if (!\file_exists($composerJsonPath)) {
96✔
153
            throw new Exception("The file \"{$composerJsonPath}\" not found");
×
154
        }
155

156
        $composerJson = json($composerJsonPath);
96✔
157
        if (\count($composerJson) <= 1) {
96✔
158
            throw new Exception("The file \"{$composerJsonPath}\" is empty");
×
159
        }
160

161
        $this->_("Composer JSON file found: <info>{$composerJsonPath}</info>", OutLvl::DEBUG);
96✔
162

163
        // Validate "composer.lock" path and file
164
        $composerLockPath = "{$realRootPath}/composer.lock";
96✔
165
        if (!\file_exists($composerLockPath)) {
96✔
166
            throw new Exception("The file \"{$composerLockPath}\" not found");
×
167
        }
168

169
        $composerLock = json($composerLockPath);
96✔
170
        if (\count($composerLock) <= 1) {
96✔
171
            throw new Exception("The file \"{$composerLockPath}\" is empty");
×
172
        }
173

174
        $this->_("Composer LOCK file found: <info>{$composerLockPath}</info>", OutLvl::DEBUG);
96✔
175

176
        return [$composerJson, $composerLock];
96✔
177
    }
178

179
    private function findVendorDir(JSON $composerJson): ?string
180
    {
181
        $realRootPath = $this->getRootPath();
96✔
182

183
        $vendorDir = $composerJson->find('config.vendor-dir') ?? 'vendor';
96✔
184

185
        $realVendorDir = \realpath("{$realRootPath}/{$vendorDir}");
96✔
186
        if (
187
            $realVendorDir !== false
96✔
188
            && \is_dir($realVendorDir)
96✔
189
        ) {
190
            return $realVendorDir;
×
191
        }
192

193
        return null;
96✔
194
    }
195
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc