Completed
Push — master ( a19dc4...d676b1 )
by Dispositif
02:25
created

GoogleLivresTemplate::createFromURL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 17
rs 9.9666
1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application
4
 * 2019 : Philippe M. <[email protected]>
5
 * For the full copyright and MIT license information, please view the LICENSE file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Domain\Models\Wiki;
11
12
use DomainException;
13
use Exception;
14
15
/**
16
 * https://fr.wikipedia.org/wiki/Mod%C3%A8le:Google_Livres
17
 * Le premier paramètre (ou id) est obligatoire. L
18
 * Le deuxième (ou titre) est requis si on ne veut pas fabriquer le lien brut (inclusion {{ouvrage}} 'Lire en ligne')
19
 * Class GoogleLivresTemplate.
20
 */
21
class GoogleLivresTemplate extends AbstractWikiTemplate
22
{
23
    const DEFAULT_GOOGLEBOOK_URL = 'https://books.google.com/books';
24
25
    const ALLOW_USER_ORDER = false;
26
27
    const MODEL_NAME = 'Google Livres';
28
29
    const REQUIRED_PARAMETERS = ['id' => ''];
30
31
    const PARAM_ALIAS
32
        = [
33
            '1' => 'id',
34
            '2' => 'titre',
35
            'surligné' => 'surligne',
36
            'BuchID' => 'id',
37
        ];
38
39
    protected $parametersByOrder
40
        = ['id', 'titre', 'couv', 'page', 'romain', 'page autre', 'surligne'];
41
42
    /**
43
     * Serialize the wiki-template.
44
     * Improvement : force param order : id/titre/...
45
     *
46
     * @param bool|null $cleanOrder
47
     *
48
     * @return string
49
     */
50
    public function serialize(?bool $cleanOrder = true): string
51
    {
52
        $text = parent::serialize();
53
54
        // Documentation suggère non affichage de ces 2 paramètres
55
        return str_replace(['id=', 'titre='], '', $text);
56
    }
57
58
    /**
59
     * Create {Google Book} from URL.
60
     * See also https://fr.wikipedia.org/wiki/Utilisateur:Jack_ma/GB
61
     * https://stackoverflow.com/questions/11584551/need-information-on-query-parameters-for-google-books-e-g-difference-between-d.
62
     *
63
     * @param string $url
64
     *
65
     * @return GoogleLivresTemplate|null
66
     * @throws Exception
67
     */
68
    public static function createFromURL(string $url): ?self
69
    {
70
        if (!self::isGoogleBookURL($url)) {
71
            throw new DomainException('not a Google Book URL');
72
        }
73
        $gooDat = self::parseGoogleBookQuery($url);
74
75
        if (empty($gooDat['id'])) {
76
            throw new DomainException("no GoogleBook 'id' in URL");
77
        }
78
79
        $data = self::mapGooData($gooDat);
80
81
        $templ = new self();
82
        $templ->hydrate($data);
83
84
        return $templ;
85
    }
86
87
    /**
88
     * Mapping Google URL data to {Google Livres} data.
89
     *
90
     * @param array $gooData
91
     *
92
     * @return array
93
     */
94
    private static function mapGooData(array $gooData): array
95
    {
96
        $data = [];
97
        $data['id'] = $gooData['id'];
98
99
        // show cover ?
100
        if (isset($gooData['printsec']) && 'frontcover' === $gooData['printsec']) {
101
            $data['couv'] = '1';
102
        }
103
104
        // page number
105
        if (!empty($gooData['pg'])) {
106
            $data['page autre'] = $gooData['pg'];
107
108
            //  pg=PAx => "page=x"
109
            if (preg_match('/^PA([0-9]+)$/', $gooData['pg'], $matches) > 0) {
110
                $data['page'] = $matches[1];
111
                unset($data['page autre']);
112
            }
113
            //  pg=PRx => "page=x|romain=1"
114
            if (preg_match('/^PR([0-9]+)$/', $gooData['pg'], $matches) > 0) {
115
                $data['page'] = $matches[1];
116
                $data['romain'] = '1';
117
                unset($data['page autre']);
118
            }
119
        }
120
121
        if (!empty($gooData['dq']) || !empty($gooData['q'])) {
122
            $data['surligne'] = $gooData['dq'] ?? $gooData['q'];
123
            $data['surligne'] = self::googleUrlEncode($data['surligne']);
124
        }
125
126
        return $data;
127
    }
128
129
    /**
130
     * Instead of url_encode(). No UTF-8 encoding.
131
     */
132
    private static function googleUrlEncode(string $str): string
133
    {
134
        return str_replace(' ', '+', trim(urldecode($str)));
135
    }
136
137
    /**
138
     * Clean the google book URL from optional&tracking data.
139
     *
140
     * @param string $url
141
     *
142
     * @return string URL
143
     */
144
    public static function simplifyGoogleUrl(string $url): string
145
    {
146
        if (!self::isGoogleBookURL($url)) {
147
            throw new DomainException('not a Google Book URL');
148
        }
149
150
        $gooDat = self::parseGoogleBookQuery($url);
151
        if (empty($gooDat['id'])) {
152
            throw new DomainException("no GoogleBook 'id' in URL");
153
        }
154
155
        $dat = [];
156
        // keep only a few parameters (+'q' ?)
157
        $keeps = ['id', 'pg', 'printsec', 'dq'];
158
        foreach ($keeps as $keep) {
159
            if (!empty($gooDat[$keep])) {
160
                $dat[$keep] = $gooDat[$keep];
161
            }
162
        }
163
164
        $googleURL = self::DEFAULT_GOOGLEBOOK_URL;
165
166
        // domain .com .fr
167
        $gooDomain = self::parseGoogleDomain($url);
168
        if ($gooDomain) {
169
            $googleURL = str_replace('.com', $gooDomain, $googleURL);
170
        }
171
172
        return $googleURL.'?'.http_build_query($dat);
173
    }
174
175
    /**
176
     * Parse URL argument from ?query and #fragment.
177
     *
178
     * @param string $url
179
     *
180
     * @return array
181
     */
182
    private static function parseGoogleBookQuery(string $url): array
183
    {
184
        // Note : Also datas in URL after the '#' !!! (URL fragment)
185
        $queryData = parse_url($url, PHP_URL_QUERY); // after ?
186
        $fragmentData = parse_url($url, PHP_URL_FRAGMENT); // after #
187
        // queryData precedence over fragmentData
188
        parse_str(implode('&', [$fragmentData, $queryData]), $val);
189
190
        return $val;
191
    }
192
193
    /**
194
     * return '.fr' or '.com'.
195
     *
196
     * @param string $url
197
     *
198
     * @return string|null
199
     */
200
    private static function parseGoogleDomain(string $url): ?string
201
    {
202
        $host = parse_url($url, PHP_URL_HOST);
203
        if (!empty($host) && preg_match('#\.[a-z]{2,3}$#', $host, $matches) > 0) {
204
            return $matches[0]; // .fr
205
        }
206
207
        return null;
208
    }
209
210
    /**
211
     * Check google URL pattern.
212
     *
213
     * @param string $text
214
     *
215
     * @return bool
216
     */
217
    public static function isGoogleBookURL(string $text): bool
218
    {
219
        if (preg_match('#^https?://(books|play)\.google\.[a-z]{2,3}/(books)?(/reader)?\?id=#i', $text) > 0) {
220
            return true;
221
        }
222
223
        return false;
224
    }
225
226
    /**
227
     * Check if Google URL or wiki {Google Books} template.
228
     *
229
     * @param string $text
230
     *
231
     * @return bool
232
     */
233
    public static function isGoogleBookValue(string $text): bool
234
    {
235
        if (true === self::isGoogleBookURL($text)) {
236
            return true;
237
        }
238
        if (preg_match('#^{{[ \n]*Google (Livres|Books)[^}]+}}$#i', $text) > 0) {
239
            return true;
240
        }
241
242
        return false;
243
    }
244
}
245