Passed
Push — master ( dff8a4...2556d0 )
by Dispositif
08:19
created

ExternMapper::postProcess()   C

Complexity

Conditions 13
Paths 64

Size

Total Lines 35
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 15.6406

Importance

Changes 0
Metric Value
cc 13
eloc 18
c 0
b 0
f 0
nc 64
nop 1
dl 0
loc 35
ccs 3
cts 4
cp 0.75
crap 15.6406
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * This file is part of dispositif/wikibot application (@github)
4
 * 2019-2023 © Philippe M./Irønie  <[email protected]>
5
 * For the full copyright and MIT license information, view the license file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Domain\Publisher;
11
12
use App\Domain\Utils\ArrayProcessTrait;
13
use App\Domain\Utils\TextUtil;
14
use Psr\Log\LoggerInterface;
15
16
/**
17
 * Generic mapper for press/revue article on web.
18
 * Using JSON-LD and meta tags to obtain {article} data.
19
 * Generic mapper for web pages URL to wiki-template references.
20
 * Converting to {article}, {lien web} or {lien brisé}
21
 * Using JSON-LD, Open Graph and Dublin Core meta extracted from HTML.
22
 */
23
class ExternMapper implements MapperInterface
24
{
25
    use ArrayProcessTrait, ExternConverterTrait;
26
27
    public const TITLE_TO_VERIFY_COMMENT = '<!-- Vérifiez ce titre -->';
28
29
    /** @var LoggerInterface */
30
    private $log;
31
    /** @var array */
32
    private $options = [];
33
    /** @var bool */
34 5
    private $titleFromHtmlState = false;
35
36 5
    public function __construct(LoggerInterface $log, ?array $options = [])
37 5
    {
38
        $this->log = $log;
39 5
        $this->options = $options;
40
    }
41 5
42
    public function process($data): array
43 5
    {
44
        $parsedData = $this->processMapping($data);
45
46 5
        return ($parsedData === []) ? [] : $this->postProcess($parsedData);
47
    }
48 5
49 5
    protected function processMapping($data): array
50 5
    {
51 3
        $mapJson = [];
52
        $mapMeta = [];
53 5
        $this->titleFromHtmlState = false;
54 5
55
        if (!empty($data['JSON-LD'])) {
56
            $mapJson = $this->processJsonLDMapping($data['JSON-LD']);
57
        }
58 5
        if (!empty($data['meta'])) {
59 3
            $openGraphMapper = new OpenGraphMapper($this->options); // todo inject/extract to reduce instanciationS ?
60 2
            $mapMeta = $openGraphMapper->process($data['meta']);
61 2
            $this->titleFromHtmlState = $openGraphMapper->isTitleFromHtmlState();
62
        }
63
64 3
        // langue absente JSON-LD mais array_merge risqué (doublon)
65
        if ($mapJson !== []) {
66
            if (!isset($mapJson['langue']) && isset($mapMeta['langue'])) {
67 2
                $mapJson['langue'] = $mapMeta['langue'];
68
                $mapJson['DATA-TYPE'] = 'JSON-LD+META';
69
            }
70
            // récupère "accès url" de OpenGraph (prévaut sur JSON:'isAccessibleForFree'
71
            if (isset($mapMeta['accès url'])) {
72
                $mapJson['accès url'] = $mapMeta['accès url'];
73
                $mapJson['DATA-TYPE'] = 'JSON-LD+META';
74
            }
75
76
            return $mapJson;
77 3
        }
78
79 3
        return $mapMeta;
80 2
    }
81
82
    /**
83 1
     * todo move to mapper ?
84 1
     */
85 1
    private function processJsonLDMapping(array $LDdata): array
86
    {
87
        if ($this->checkJSONLD($LDdata)) {
88
            return (new JsonLDMapper())->process($LDdata);
89
        }
90
        // gestion des multiples objets comme Figaro
91
        foreach ($LDdata as $dat) {
92 3
            if (is_array($dat) && $this->checkJSONLD($dat)) {
93
                return (new JsonLDMapper())->process($dat);
94 3
            }
95
        }
96
97
        return [];
98
    }
99
100
    protected function checkJSONLD(array $jsonLD): bool
101
    {
102
        return isset($jsonLD['headline']) && isset($jsonLD['@type']);
103
    }
104
105 5
    /**
106
     * Data sanitization.
107 5
     * todo Config parameter for post-process
108 5
     */
109 3
    protected function postProcess(array $data): array
110
    {
111
        $this->log->debug('call ExternMapper::postProcess()');
112
        $data = $this->deleteEmptyValueArray($data);
113 5
        if (isset($data['langue']) && 'fr' === $data['langue']) {
114
            unset($data['langue']);
115
        }
116
117 5
        // Ça m'énerve ! Gallica met "vidéo" pour livre numérisé
118
        if (isset($data['site']) && $data['site'] === 'Gallica') {
119
            unset($data['format']);
120
        }
121
        if (isset($data['site']) && TextUtil::countAllCapsWords($data['site']) >= 3) {
122
            $this->log->debug('lowercase site name');
123
            $data['site'] = TextUtil::mb_ucfirst(mb_strtolower($data['site']));
124
        }
125
126
        // lowercase title if too many ALLCAPS words
127
        if (isset($data['titre']) && TextUtil::countAllCapsWords($data['titre']) >= 4) {
128
            $this->log->debug('lowercase title');
129
            $data['titre'] = TextUtil::mb_ucfirst(mb_strtolower($data['titre']));
130
        }
131
132
        // SEO : cut site name if too long if no domain.name and no wiki link
133
        if (isset($data['site']) && false === mb_strpos($data['site'], '.') && false === mb_strpos($data['site'], '[[')) {
134
            $data['site'] = TextUtil::cutTextOnSpace($data['site'], 40);
135
        }
136
137
        // title has 150 chars max, or is cut with "…" at the end
138
        if (isset($data['titre'])) {
139
            $data['titre'] = TextUtil::cutTextOnSpace($data['titre'], 150);
140
            $data['titre'] = $this->addVerifyCommentIfNecessary($data['titre']);
141
        }
142
143
        return $data;
144
    }
145
146
    /**
147
     * todo Créer un modèle {titre à vérifier} ?
148
     */
149
    private function addVerifyCommentIfNecessary(?string $title): ?string
150
    {
151
        if (
152
            !empty($title)
153
            && strlen($title) >= 30
154
            && true === $this->titleFromHtmlState
155
        ) {
156
            /** @noinspection PhpRedundantOptionalArgumentInspection */
157
            $title = TextUtil::cutTextOnSpace($title, 70);
158
            $title .= self::TITLE_TO_VERIFY_COMMENT;
159
        }
160
161
        return $title;
162
    }
163
}
164