Passed
Push — master ( 02c0e5...5db183 )
by Stefan
01:17 queued 11s
created

StatsListCommand::shareDataWithShift()   B

Complexity

Conditions 8
Paths 11

Size

Total Lines 41
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 8
eloc 22
c 7
b 0
f 0
nc 11
nop 1
dl 0
loc 41
rs 8.4444
1
<?php declare(strict_types=1);
2
3
namespace Wnx\LaravelStats\Console;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Support\Str;
7
use Wnx\LaravelStats\ClassesFinder;
8
use Wnx\LaravelStats\Outputs\AsciiTableOutput;
9
use Wnx\LaravelStats\Outputs\JsonOutput;
10
use Wnx\LaravelStats\Project;
11
use Wnx\LaravelStats\ReflectionClass;
12
use Wnx\LaravelStats\RejectionStrategies\RejectVendorClasses;
13
use Wnx\LaravelStats\ShareableMetrics\CollectMetrics;
14
use Wnx\LaravelStats\ShareableMetrics\ProjectName;
15
use Wnx\LaravelStats\ShareableMetrics\SendToLaravelShift;
16
17
class StatsListCommand extends Command
18
{
19
    /**
20
     * The name and signature of the console command.
21
     *
22
     * @var string
23
     */
24
    protected $signature = 'stats
25
                            {--json : Output the statistics as JSON}
26
                            {-c|--components= : Comma separated list of components which should be displayed}
27
                            {--s|share : Share project statistic with Laravel community <https://stats.laravelshift.com>}
28
                            {--name= : Name used when sharing project statistic}
29
                            {--payload : Output payload to be shared with Laravel community <https://stats.laravelshift.com>}
30
                            {--dry-run : Do not make request to share statistic}';
31
32
    /**
33
     * The console command description.
34
     *
35
     * @var string
36
     */
37
    protected $description = 'Generate statistics for this Laravel project';
38
39
    /**
40
     * Execute the console command.
41
     *
42
     * @return void
43
     */
44
    public function handle()
45
    {
46
        $classes = app(ClassesFinder::class)->findAndLoadClasses();
47
48
        // Transform  Classes into ReflectionClass instances
49
        // Remove Classes based on the RejectionStrategy
50
        // Remove Classes based on the namespace
51
        $reflectionClasses = $classes->map(function ($class) {
52
            return new ReflectionClass($class);
53
        })->reject(function (ReflectionClass $class) {
54
            return app(config('stats.rejection_strategy', RejectVendorClasses::class))
55
                ->shouldClassBeRejected($class);
0 ignored issues
show
Bug introduced by
The method shouldClassBeRejected() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

55
                ->/** @scrutinizer ignore-call */ shouldClassBeRejected($class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56
        })->reject(function (ReflectionClass $class) {
57
            foreach (config('stats.ignored_namespaces', []) as $namespace) {
58
                if (Str::startsWith($class->getNamespaceName(), $namespace)) {
59
                    return true;
60
                }
61
            }
62
63
            return false;
64
        });
65
66
        $project = new Project($reflectionClasses);
67
68
        $this->renderOutput($project);
69
70
        if ($this->option('share') === true) {
0 ignored issues
show
introduced by
The condition $this->option('share') === true is always false.
Loading history...
71
            $this->shareDataWithShift($project);
72
        }
73
    }
74
75
    private function getArrayOfComponentsToDisplay(): array
76
    {
77
        if (is_null($this->option('components'))) {
78
            return [];
79
        }
80
81
        return explode(',', $this->option('components'));
0 ignored issues
show
Bug introduced by
It seems like $this->option('components') can also be of type string[]; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
        return explode(',', /** @scrutinizer ignore-type */ $this->option('components'));
Loading history...
82
    }
83
84
    private function renderOutput(Project $project)
85
    {
86
        if ($this->option('json') === true) {
0 ignored issues
show
introduced by
The condition $this->option('json') === true is always false.
Loading history...
87
            $json = (new JsonOutput())->render(
88
                $project,
89
                $this->option('verbose'),
90
                $this->getArrayOfComponentsToDisplay()
91
            );
92
93
            if ($this->option('payload') !== true) {
94
                $this->output->text(json_encode($json));
95
            }
96
        } elseif ($this->option('payload') !== true) {
0 ignored issues
show
introduced by
The condition $this->option('payload') !== true is always true.
Loading history...
97
            (new AsciiTableOutput($this->output))->render(
98
                $project,
99
                $this->option('verbose'),
100
                $this->getArrayOfComponentsToDisplay()
101
            );
102
        }
103
    }
104
105
    private function shareDataWithShift(Project $project): void
106
    {
107
        $metrics = app(CollectMetrics::class)->collect($project);
108
109
        $defaultValueForConfirmation = $this->option('no-interaction') ?? false;
110
111
        if ($this->confirm("Do you want to share stats above from your project with the Laravel Community to stats.laravelshift.com?", $defaultValueForConfirmation)) {
112
            $projectName = $this->getProjectName();
113
114
            if ($projectName === null) {
115
                $this->error("Please provide a project name.");
116
                return;
117
            }
118
119
            if (! Str::contains($projectName, '/')) {
120
                $this->error("Please use the organisation/repository schema for naming your project.");
121
                return;
122
            }
123
124
            $payload = $metrics->toHttpPayload($projectName);
125
126
            if ($this->option('payload')) {
127
                $this->output->text(json_encode($payload));
128
            }
129
130
            if ($this->option('dry-run')) {
131
                return;
132
            }
133
134
            $wasSuccessful = app(SendToLaravelShift::class)->send($metrics->toHttpPayload($projectName));
135
136
            if ($this->option('payload')) {
137
                return;
138
            }
139
140
            if ($wasSuccessful) {
141
                $this->info("Thanks for sharing your project statistic with the community!");
142
                return;
143
            }
144
145
            $this->error("Unable to share stats. (Check logs for details)");
146
        }
147
    }
148
149
    private function getProjectName(): ?string
150
    {
151
        if ($this->option('name')) {
152
            return $this->option('name');
153
        }
154
155
        if (app(ProjectName::class)->hasStoredProjectName() === false) {
156
            $generatedProjectName = app(ProjectName::class)->determineProjectNameFromGit();
157
158
            $projectName = $this->ask("We've determined the following name for your project: \"{$generatedProjectName}\".\n Type a new name or leave it blank to continue.", $generatedProjectName);
159
160
            app(ProjectName::class)->storeNameInRcFile($projectName);
161
162
            return $projectName;
163
        }
164
165
        return app(ProjectName::class)->get();
166
    }
167
}
168