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

eliashaeussler / composer-update-check / 13955603431

19 Mar 2025 02:58PM UTC coverage: 20.538%. First build
13955603431

Pull #130

github

web-flow
[TASK] Update phpstan/phpstan-symfony to v2.0.3

| datasource | package                 | from  | to    |
| ---------- | ----------------------- | ----- | ----- |
| packagist  | phpstan/phpstan-symfony | 2.0.2 | 2.0.3 |
Pull Request #130: [!!!][FEATURE] Modernize plugin

356 of 1832 new or added lines in 57 files covered. (19.43%)

382 of 1860 relevant lines covered (20.54%)

1.11 hits per line

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

0.0
/src/Entity/Report/TeamsReport.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Composer package "eliashaeussler/composer-update-check".
7
 *
8
 * Copyright (C) 2020-2025 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\ComposerUpdateCheck\Entity\Report;
25

26
use EliasHaeussler\ComposerUpdateCheck\Entity;
27
use JsonSerializable;
28

29
use function sprintf;
30

31
/**
32
 * TeamsReport.
33
 *
34
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
35
 * @license GPL-3.0-or-later
36
 */
37
final class TeamsReport implements JsonSerializable
38
{
39
    private const SECURITY_ADVISORIES_CONTAINER_ID = 'securityAdvisories';
40

41
    /**
42
     * @param list<Dto\TeamsAttachment> $attachments
43
     */
NEW
44
    private function __construct(
×
45
        public readonly string $type,
46
        public readonly array $attachments,
NEW
47
    ) {}
×
48

NEW
49
    public static function create(Entity\Result\UpdateCheckResult $result, string $additionalData = ''): self
×
50
    {
NEW
51
        return new self(
×
NEW
52
            'message',
×
NEW
53
            self::createAttachments($result, $additionalData),
×
NEW
54
        );
×
55
    }
56

57
    /**
58
     * @return list<Dto\TeamsAttachment>
59
     */
NEW
60
    private static function createAttachments(Entity\Result\UpdateCheckResult $result, string $additionalData = ''): array
×
61
    {
NEW
62
        $attachment = Dto\TeamsAttachment::adaptiveCard(
×
NEW
63
            self::createCardBody($result),
×
NEW
64
            self::createFallbackText($result),
×
NEW
65
            self::createCardActions($result),
×
NEW
66
            $additionalData,
×
NEW
67
        );
×
68

NEW
69
        return [$attachment];
×
70
    }
71

72
    /**
73
     * @return list<Dto\TeamsContent>
74
     */
NEW
75
    private static function createCardBody(Entity\Result\UpdateCheckResult $result): array
×
76
    {
NEW
77
        $rootPackageName = $result->getRootPackage()?->getName();
×
NEW
78
        $numberOfOutdatedPackages = 0;
×
NEW
79
        $numberOfInsecurePackages = 0;
×
NEW
80
        $highestSeverityLevels = [];
×
81

82
        // Count outdated and insecure packages
NEW
83
        foreach ($result->getOutdatedPackages() as $outdatedPackage) {
×
NEW
84
            ++$numberOfOutdatedPackages;
×
85

NEW
86
            if ($outdatedPackage->isInsecure()) {
×
NEW
87
                ++$numberOfInsecurePackages;
×
NEW
88
                $highestSeverityLevels[] = $outdatedPackage->getHighestSeverityLevel();
×
89
            }
90
        }
91

92
        // Add title
NEW
93
        $contents = [
×
NEW
94
            Dto\TeamsContent::textBlock(
×
NEW
95
                text: sprintf(
×
NEW
96
                    '%d package%s%s %s outdated',
×
NEW
97
                    $numberOfOutdatedPackages,
×
NEW
98
                    1 !== $numberOfOutdatedPackages ? 's' : '',
×
NEW
99
                    $numberOfInsecurePackages > 0 ? sprintf(' (%d insecure)', $numberOfInsecurePackages) : '',
×
NEW
100
                    1 !== $numberOfOutdatedPackages ? 'are' : 'is',
×
NEW
101
                ),
×
NEW
102
                wrap: true,
×
NEW
103
                size: Enum\TeamsFontSize::Large,
×
NEW
104
                weight: Enum\TeamsFontWeight::Bolder,
×
NEW
105
            ),
×
NEW
106
        ];
×
107

108
        // Add summary
NEW
109
        if (null !== $rootPackageName) {
×
NEW
110
            $contents[] = Dto\TeamsContent::textBlock(
×
NEW
111
                text: sprintf(
×
NEW
112
                    'Project **%s** has **%d outdated package%s**.',
×
NEW
113
                    $rootPackageName,
×
NEW
114
                    $numberOfOutdatedPackages,
×
NEW
115
                    1 !== $numberOfOutdatedPackages ? 's' : '',
×
NEW
116
                ),
×
NEW
117
                wrap: true,
×
NEW
118
            );
×
119

NEW
120
            if ([] !== $highestSeverityLevels) {
×
NEW
121
                $highestSeverityLevel = Entity\Security\SeverityLevel::getHighestSeverityLevel(...$highestSeverityLevels);
×
NEW
122
                $contents[] = Dto\TeamsContent::textBlock(
×
NEW
123
                    text: sprintf(
×
NEW
124
                        '%s marked as **insecure** with a highest severity of **%s %s**.',
×
NEW
125
                        $numberOfInsecurePackages === $numberOfOutdatedPackages
×
NEW
126
                            ? ($numberOfInsecurePackages > 1 ? 'They are' : 'It is')
×
NEW
127
                            : sprintf(
×
NEW
128
                                '%d of them %s',
×
NEW
129
                                $numberOfInsecurePackages,
×
NEW
130
                                $numberOfInsecurePackages > 1 ? 'are' : 'is',
×
NEW
131
                            ),
×
NEW
132
                        self::getEmojiForSeverityLevel($highestSeverityLevel),
×
NEW
133
                        $highestSeverityLevel->value,
×
NEW
134
                    ),
×
NEW
135
                    wrap: true,
×
NEW
136
                    spacing: Enum\TeamsSpacing::None,
×
NEW
137
                );
×
138
            }
139
        }
140

141
        // Add table with outdated packages
NEW
142
        $contents[] = self::createTableWithOutdatedPackages($result);
×
143

144
        // Add container with security advisories
NEW
145
        if ($numberOfInsecurePackages > 0) {
×
NEW
146
            $contents[] = self::createSecurityAdvisoriesContainer($result);
×
147
        }
148

NEW
149
        return $contents;
×
150
    }
151

NEW
152
    private static function createTableWithOutdatedPackages(Entity\Result\UpdateCheckResult $result): Dto\TeamsContent
×
153
    {
NEW
154
        $hasInsecureOutdatedPackages = $result->hasInsecureOutdatedPackages();
×
NEW
155
        $columns = [];
×
NEW
156
        $rowCells = [];
×
NEW
157
        $rowHeaders = [
×
NEW
158
            'Package' => 2,
×
NEW
159
            'Current version' => 1,
×
NEW
160
            'New version' => 1,
×
NEW
161
        ];
×
162

NEW
163
        if ($hasInsecureOutdatedPackages) {
×
NEW
164
            $rowHeaders['Security advisory'] = 1;
×
165
        }
166

NEW
167
        foreach ($rowHeaders as $rowHeader => $columnWidth) {
×
NEW
168
            $columns[] = new Dto\TeamsTableColumn($columnWidth);
×
NEW
169
            $rowCells[] = new Dto\TeamsTableCell(
×
NEW
170
                [
×
NEW
171
                    Dto\TeamsContent::textBlock(
×
NEW
172
                        text: $rowHeader,
×
NEW
173
                        wrap: true,
×
NEW
174
                    ),
×
NEW
175
                ],
×
NEW
176
            );
×
177
        }
178

NEW
179
        $rows = [
×
NEW
180
            new Dto\TeamsTableRow(
×
NEW
181
                cells: $rowCells,
×
NEW
182
                spacing: 'None',
×
NEW
183
                horizontalCellContentAlignment: 'Left',
×
NEW
184
                verticalCellContentAlignment: 'Top',
×
NEW
185
            ),
×
NEW
186
        ];
×
187

188
        // Add table rows
NEW
189
        foreach ($result->getOutdatedPackages() as $outdatedPackage) {
×
NEW
190
            $cells = [
×
NEW
191
                new Dto\TeamsTableCell(
×
NEW
192
                    [
×
NEW
193
                        Dto\TeamsContent::textBlock(
×
NEW
194
                            text: sprintf('[%s](%s)', $outdatedPackage->getName(), $outdatedPackage->getProviderLink()),
×
NEW
195
                            wrap: true,
×
NEW
196
                        ),
×
NEW
197
                    ],
×
NEW
198
                ),
×
NEW
199
                new Dto\TeamsTableCell(
×
NEW
200
                    [
×
NEW
201
                        Dto\TeamsContent::textBlock(
×
NEW
202
                            text: $outdatedPackage->getOutdatedVersion()->toString(),
×
NEW
203
                            wrap: true,
×
NEW
204
                        ),
×
NEW
205
                    ],
×
NEW
206
                ),
×
NEW
207
                new Dto\TeamsTableCell(
×
NEW
208
                    [
×
NEW
209
                        Dto\TeamsContent::textBlock(
×
NEW
210
                            text: $outdatedPackage->getNewVersion()->toString(),
×
NEW
211
                            wrap: true,
×
NEW
212
                            weight: Enum\TeamsFontWeight::Bolder,
×
NEW
213
                        ),
×
NEW
214
                    ],
×
NEW
215
                ),
×
NEW
216
            ];
×
217

NEW
218
            if ($outdatedPackage->isInsecure()) {
×
NEW
219
                $severityLevel = $outdatedPackage->getHighestSeverityLevel();
×
NEW
220
                $cells[] = new Dto\TeamsTableCell(
×
NEW
221
                    [
×
NEW
222
                        Dto\TeamsContent::textBlock(
×
NEW
223
                            text: sprintf(
×
NEW
224
                                '%s %s',
×
NEW
225
                                self::getEmojiForSeverityLevel($severityLevel),
×
NEW
226
                                $severityLevel->value,
×
NEW
227
                            ),
×
NEW
228
                            wrap: true,
×
NEW
229
                        ),
×
NEW
230
                    ],
×
NEW
231
                );
×
NEW
232
            } elseif ($hasInsecureOutdatedPackages) {
×
NEW
233
                $cells[] = new Dto\TeamsTableCell(
×
NEW
234
                    [
×
NEW
235
                        Dto\TeamsContent::textBlock(''),
×
NEW
236
                    ],
×
NEW
237
                );
×
238
            }
239

NEW
240
            $rows[] = new Dto\TeamsTableRow($cells);
×
241
        }
242

NEW
243
        return Dto\TeamsContent::table(
×
NEW
244
            columns: $columns,
×
NEW
245
            rows: $rows,
×
NEW
246
            firstRowAsHeader: true,
×
NEW
247
            gridStyle: 'default',
×
NEW
248
        );
×
249
    }
250

NEW
251
    private static function createSecurityAdvisoriesContainer(Entity\Result\UpdateCheckResult $result): Dto\TeamsContent
×
252
    {
NEW
253
        $items = [
×
NEW
254
            Dto\TeamsContent::textBlock(
×
NEW
255
                text: 'Security advisories',
×
NEW
256
                wrap: true,
×
NEW
257
                size: Enum\TeamsFontSize::Large,
×
NEW
258
                weight: Enum\TeamsFontWeight::Bolder,
×
NEW
259
            ),
×
NEW
260
        ];
×
261

NEW
262
        foreach ($result->getInsecureOutdatedPackages() as $insecurePackage) {
×
NEW
263
            foreach ($insecurePackage->getSecurityAdvisories() as $securityAdvisory) {
×
NEW
264
                $facts = [
×
NEW
265
                    new Dto\TeamsFact('Package', $securityAdvisory->getPackageName()),
×
NEW
266
                    new Dto\TeamsFact('Reported at', $securityAdvisory->getReportedAt()->format('Y-m-d')),
×
NEW
267
                ];
×
268

NEW
269
                if (null !== $securityAdvisory->getCVE()) {
×
NEW
270
                    $facts[] = new Dto\TeamsFact('CVE', $securityAdvisory->getCVE());
×
271
                }
272

NEW
273
                $containerItems = [
×
NEW
274
                    Dto\TeamsContent::textBlock(
×
NEW
275
                        text: sprintf(
×
NEW
276
                            '%s %s',
×
NEW
277
                            self::getEmojiForSeverityLevel($securityAdvisory->getSeverity()),
×
NEW
278
                            $securityAdvisory->getSanitizedTitle(),
×
NEW
279
                        ),
×
NEW
280
                        wrap: true,
×
NEW
281
                        weight: Enum\TeamsFontWeight::Bolder,
×
NEW
282
                    ),
×
NEW
283
                    Dto\TeamsContent::factSet(
×
NEW
284
                        facts: $facts,
×
NEW
285
                        spacing: Enum\TeamsSpacing::Small,
×
NEW
286
                    ),
×
NEW
287
                ];
×
288

NEW
289
                if (null !== $securityAdvisory->getLink()) {
×
NEW
290
                    $containerItems[] = Dto\TeamsContent::textBlock(
×
NEW
291
                        text: sprintf('[Read more](%s)', $securityAdvisory->getLink()),
×
NEW
292
                        wrap: true,
×
NEW
293
                    );
×
294
                }
295

NEW
296
                $items[] = Dto\TeamsContent::container($containerItems);
×
297
            }
298
        }
299

NEW
300
        return Dto\TeamsContent::container(
×
NEW
301
            items: $items,
×
NEW
302
            isVisible: false,
×
NEW
303
            id: self::SECURITY_ADVISORIES_CONTAINER_ID,
×
NEW
304
            spacing: Enum\TeamsSpacing::Medium,
×
NEW
305
        );
×
306
    }
307

NEW
308
    private static function createFallbackText(Entity\Result\UpdateCheckResult $result): string
×
309
    {
NEW
310
        $rootPackageName = $result->getRootPackage()?->getName();
×
NEW
311
        $numberOfOutdatedPackages = 0;
×
NEW
312
        $numberOfInsecurePackages = 0;
×
NEW
313
        $highestSeverityLevels = [];
×
NEW
314
        $addition = '';
×
315

316
        // Count outdated and insecure packages
NEW
317
        foreach ($result->getOutdatedPackages() as $outdatedPackage) {
×
NEW
318
            ++$numberOfOutdatedPackages;
×
319

NEW
320
            if ($outdatedPackage->isInsecure()) {
×
NEW
321
                ++$numberOfInsecurePackages;
×
NEW
322
                $highestSeverityLevels[] = $outdatedPackage->getHighestSeverityLevel();
×
323
            }
324
        }
325

NEW
326
        if ([] !== $highestSeverityLevels) {
×
NEW
327
            $highestSeverityLevel = Entity\Security\SeverityLevel::getHighestSeverityLevel(...$highestSeverityLevels);
×
NEW
328
            $addition = sprintf(
×
NEW
329
                ' %s marked as insecure with a highest severity of "%s".',
×
NEW
330
                $numberOfInsecurePackages === $numberOfOutdatedPackages
×
NEW
331
                    ? ($numberOfInsecurePackages > 1 ? 'They are' : 'It is')
×
NEW
332
                    : sprintf(
×
NEW
333
                        '%d of them %s',
×
NEW
334
                        $numberOfInsecurePackages,
×
NEW
335
                        $numberOfInsecurePackages > 1 ? 'are' : 'is',
×
NEW
336
                    ),
×
NEW
337
                $highestSeverityLevel->value,
×
NEW
338
            );
×
339
        }
340

NEW
341
        if (null !== $rootPackageName) {
×
NEW
342
            return sprintf(
×
NEW
343
                'Project "%s" has %d outdated package%s.%s',
×
NEW
344
                $rootPackageName,
×
NEW
345
                $numberOfOutdatedPackages,
×
NEW
346
                1 !== $numberOfOutdatedPackages ? 's' : '',
×
NEW
347
                $addition,
×
NEW
348
            );
×
349
        }
350

NEW
351
        return sprintf(
×
NEW
352
            '%d package%s are currently outdated.%s',
×
NEW
353
            $numberOfOutdatedPackages,
×
NEW
354
            1 !== $numberOfOutdatedPackages ? 's' : '',
×
NEW
355
            $addition,
×
NEW
356
        );
×
357
    }
358

359
    /**
360
     * @return list<Dto\TeamsAction>
361
     */
NEW
362
    private static function createCardActions(Entity\Result\UpdateCheckResult $result): array
×
363
    {
NEW
364
        if (!$result->hasInsecureOutdatedPackages()) {
×
NEW
365
            return [];
×
366
        }
367

NEW
368
        $actionId = 'showSecurityAdvisories';
×
369

NEW
370
        return [
×
NEW
371
            Dto\TeamsAction::toggleVisibility(
×
NEW
372
                $actionId,
×
NEW
373
                'Show security advisories',
×
NEW
374
                [
×
NEW
375
                    self::SECURITY_ADVISORIES_CONTAINER_ID,
×
NEW
376
                    $actionId,
×
NEW
377
                ],
×
NEW
378
            ),
×
NEW
379
        ];
×
380
    }
381

NEW
382
    private static function getEmojiForSeverityLevel(Entity\Security\SeverityLevel $severityLevel): string
×
383
    {
NEW
384
        return match ($severityLevel) {
×
NEW
385
            Entity\Security\SeverityLevel::Low => '⚪',
×
NEW
386
            Entity\Security\SeverityLevel::Medium => '🟡',
×
NEW
387
            Entity\Security\SeverityLevel::High => '🟠',
×
NEW
388
            Entity\Security\SeverityLevel::Critical => '🔴',
×
NEW
389
        };
×
390
    }
391

392
    /**
393
     * @return array{
394
     *     type: string,
395
     *     attachments: list<Dto\TeamsAttachment>,
396
     * }
397
     */
NEW
398
    public function jsonSerialize(): array
×
399
    {
NEW
400
        return [
×
NEW
401
            'type' => $this->type,
×
NEW
402
            'attachments' => $this->attachments,
×
NEW
403
        ];
×
404
    }
405
}
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