Completed
Push — feature-output-formats ( 244939...ff75fd )
by Arnaud
02:03
created

Page::urlize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/*
3
 * Copyright (c) Arnaud Ligny <[email protected]>
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Cecil\Collection\Page;
10
11
use Cecil\Collection\Item;
12
use Cecil\Page\Type;
13
use Cecil\Page\Parser;
14
use Cecil\Page\VariableTrait;
15
use Cocur\Slugify\Slugify;
16
use Symfony\Component\Finder\SplFileInfo;
17
18
/**
19
 * Class Page.
20
 */
21
class Page extends Item
22
{
23
    use VariableTrait;
24
25
    const SLUGIFY_PATTERN = '/(^\/|[^a-z0-9\/]|-)+/';
26
    // https://regex101.com/r/tJWUrd/1
27
    const PREFIX_PATTERN = '^(.*?)(([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])|[0-9]+)(-|_|\.)(.*)$';
28
29
    /**
30
     * @var SplFileInfo
31
     */
32
    protected $file;
33
    /**
34
     * @var string
35
     */
36
    protected $fileExtension;
37
    /**
38
     * @var string
39
     */
40
    protected $filePath;
41
    /**
42
     * @var string
43
     */
44
    protected $fileName;
45
    /**
46
     * @var string
47
     */
48
    protected $fileId;
49
    /**
50
     * @var bool
51
     */
52
    protected $virtual = false;
53
    /**
54
     * @var string
55
     */
56
    protected $type;
57
    /**
58
     * @var string
59
     */
60
    protected $id;
61
    /**
62
     * @var string
63
     */
64
    protected $pathname;
65
    /**
66
     * @var string
67
     */
68
    protected $path;
69
    /**
70
     * @var string
71
     */
72
    protected $name;
73
    /**
74
     * @var string
75
     */
76
    protected $frontmatter;
77
    /**
78
     * @var string
79
     */
80
    protected $body;
81
    /**
82
     * @var string
83
     */
84
    protected $html;
85
86
    /**
87
     * Constructor.
88
     *
89
     * @param SplFileInfo|null $file
90
     */
91
    public function __construct(SplFileInfo $file = null)
92
    {
93
        $this->file = $file;
94
95
        if ($this->file instanceof SplFileInfo) {
96
            // file extension: "md"
97
            $this->fileExtension = pathinfo($this->file, PATHINFO_EXTENSION);
98
            // file path: "Blog"
99
            $this->filePath = str_replace(DIRECTORY_SEPARATOR, '/', $this->file->getRelativePath());
100
            // file name: "Post 1"
101
            $this->fileName = basename($this->file->getBasename(), '.'.$this->fileExtension);
102
            // file id: "Blog/Post 1"
103
            $this->fileId = ($this->filePath ? $this->filePath.'/' : '')
104
                .($this->filePath && $this->fileName == 'index' ? '' : $this->fileName);
105
            /*
106
             * variables default values
107
             */
108
            // id - ie: "blog/post-1"
109
            $this->id = $this->urlize(self::subPrefix($this->fileId));
110
            // pathname - ie: "blog/post-1"
111
            $this->pathname = $this->urlize(self::subPrefix($this->fileId));
112
            // path - ie: "blog"
113
            $this->path = $this->urlize($this->filePath);
114
            // name - ie: "post-1"
115
            $this->name = $this->urlize(self::subPrefix($this->fileName));
116
            /*
117
             * front matter default values
118
             */
119
            // title - ie: "Post 1"
120
            $this->setTitle(self::subPrefix($this->fileName));
121
            // section - ie: "blog"
122
            $this->setSection(explode('/', $this->path)[0]);
123
            // date from file meta
124
            $this->setDate(filemtime($this->file->getPathname()));
125
            // file as a prefix?
126
            if (false !== self::getPrefix($this->fileId)) {
127
                // prefix is a valid date?
128
                $isValidDate = function ($date, $format = 'Y-m-d') {
129
                    $d = \DateTime::createFromFormat($format, $date);
130
131
                    return $d && $d->format($format) === $date;
132
                };
133
                if ($isValidDate(self::getPrefix($this->fileId))) {
134
                    $this->setDate((string) self::getPrefix($this->fileId));
135
                } else {
136
                    // prefix is an integer
137
                    $this->setWeight((int) self::getPrefix($this->fileId));
138
                }
139
            }
140
            // permalink
141
            $this->setPermalink($this->pathname);
142
143
            parent::__construct($this->id);
144
        } else {
145
            $this->virtual = true;
146
147
            parent::__construct();
148
        }
149
        $this->setVariable('virtual', $this->virtual);
150
        $this->setVariable('published', true);
151
        $this->setVariable('content_template', 'page.content.twig');
152
    }
153
154
    /**
155
     * Return matches array if prefix exist or false.
156
     *
157
     * @param string $string
158
     *
159
     * @return string[]|false
160
     */
161
    public static function hasPrefix(string $string)
162
    {
163
        if (preg_match('/'.self::PREFIX_PATTERN.'/', $string, $matches)) {
164
            return $matches;
165
        } else {
166
            return false;
167
        }
168
    }
169
170
    /**
171
     * Return prefix if prefix or false.
172
     *
173
     * @param string $string
174
     *
175
     * @return string[]|false
0 ignored issues
show
Documentation introduced by
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
176
     */
177
    public static function getPrefix(string $string)
178
    {
179
        if (false !== ($matches = self::hasPrefix($string))) {
180
            return $matches[2];
181
        }
182
183
        return false;
184
    }
185
186
    /**
187
     * Return string without prefix (if exist).
188
     *
189
     * @param string $string
190
     *
191
     * @return string
192
     */
193
    public static function subPrefix(string $string): string
194
    {
195
        if (false !== ($matches = self::hasPrefix($string))) {
196
            return $matches[1].$matches[7];
197
        }
198
199
        return $string;
200
    }
201
202
    /**
203
     * Format string into URL.
204
     *
205
     * @param string $string
206
     *
207
     * @return string
208
     */
209
    public static function urlize(string $string): string
210
    {
211
        return Slugify::create([
212
            'regexp' => self::SLUGIFY_PATTERN,
213
        ])->slugify($string);
214
    }
215
216
    /**
217
     * Is current page is virtual?
218
     *
219
     * @return bool
220
     */
221
    public function isVirtual(): bool
222
    {
223
        return $this->virtual;
224
    }
225
226
    /**
227
     * Set page type.
228
     *
229
     * @param string $type
230
     *
231
     * @return self
232
     */
233
    public function setType(string $type): self
234
    {
235
        $this->type = new Type($type);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Cecil\Page\Type($type) of type object<Cecil\Page\Type> is incompatible with the declared type string of property $type.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
236
237
        return $this;
238
    }
239
240
    /**
241
     * Get page type.
242
     *
243
     * @return string|null
244
     */
245
    public function getType(): ?string
246
    {
247
        return $this->type;
248
    }
249
250
    /**
251
     * Parse file content.
252
     *
253
     * @return self
254
     */
255
    public function parse(): self
256
    {
257
        $parser = new Parser($this->file);
258
        $parsed = $parser->parse();
259
        $this->frontmatter = $parsed->getFrontmatter();
260
        $this->body = $parsed->getBody();
261
262
        return $this;
263
    }
264
265
    /**
266
     * Set name.
267
     *
268
     * @param string $name
269
     *
270
     * @return self
271
     */
272
    public function setName(string $name): self
273
    {
274
        $this->name = $name;
275
276
        return $this;
277
    }
278
279
    /**
280
     * Get name.
281
     *
282
     * @return string|null
283
     */
284
    public function getName(): ?string
285
    {
286
        return $this->name;
287
    }
288
289
    /**
290
     * Set path.
291
     *
292
     * @param $path
293
     *
294
     * @return self
295
     */
296
    public function setPath(string $path): self
297
    {
298
        $this->path = $path;
299
300
        return $this;
301
    }
302
303
    /**
304
     * Get path.
305
     *
306
     * @return string
307
     */
308
    public function getPath(): string
309
    {
310
        return $this->path;
311
    }
312
313
    /**
314
     * Set path name.
315
     *
316
     * @param string $pathname
317
     *
318
     * @return self
319
     */
320
    public function setPathname(string $pathname): self
321
    {
322
        $this->pathname = $pathname;
323
324
        return $this;
325
    }
326
327
    /**
328
     * Get path name.
329
     *
330
     * @return string
331
     */
332
    public function getPathname(): string
333
    {
334
        return $this->pathname;
335
    }
336
337
    /**
338
     * Set section.
339
     *
340
     * @param string $section
341
     *
342
     * @return self
343
     */
344
    public function setSection(string $section): self
345
    {
346
        $this->setVariable('section', $section);
347
348
        return $this;
349
    }
350
351
    /**
352
     * Get section.
353
     *
354
     * @return string|false
355
     */
356
    public function getSection(): ?string
357
    {
358
        if (empty($this->getVariable('section')) && !empty($this->path)) {
359
            $this->setSection(explode('/', $this->path)[0]);
360
        }
361
362
        return $this->getVariable('section');
363
    }
364
365
    /**
366
     * Set title.
367
     *
368
     * @param string $title
369
     *
370
     * @return self
371
     */
372
    public function setTitle(string $title): self
373
    {
374
        $this->setVariable('title', $title);
375
376
        return $this;
377
    }
378
379
    /**
380
     * Get title.
381
     *
382
     * @return string|false
383
     */
384
    public function getTitle(): ?string
385
    {
386
        return $this->getVariable('title');
387
    }
388
389
    /**
390
     * Set date.
391
     *
392
     * @param string|\DateTime $date
393
     *
394
     * @return self
395
     */
396
    public function setDate($date): self
397
    {
398
        $this->setVariable('date', $date);
399
400
        return $this;
401
    }
402
403
    /**
404
     * Get Date.
405
     *
406
     * @return \DateTime|false
407
     */
408
    public function getDate(): ?\DateTime
409
    {
410
        return $this->getVariable('date');
411
    }
412
413
    /**
414
     * Set weight.
415
     *
416
     * @param int $weight
417
     *
418
     * @return self
419
     */
420
    public function setWeight(int $weight): self
421
    {
422
        $this->setVariable('weight', $weight);
423
424
        return $this;
425
    }
426
427
    /**
428
     * Get weight.
429
     *
430
     * @return int|null
431
     */
432
    public function getWeight(): ?int
433
    {
434
        return $this->getVariable('weight');
435
    }
436
437
    /**
438
     * Set permalink.
439
     *
440
     * @param string $permalink
441
     *
442
     * @return self
443
     */
444
    public function setPermalink(string $permalink): self
445
    {
446
        // https://regex101.com/r/45oTKm/1
447
        $permalink = preg_replace('/index$/i', '', $permalink);
448
449
        $this->setVariable('permalink', $permalink);
450
451
        return $this;
452
    }
453
454
    /**
455
     * Get permalink.
456
     *
457
     * @return string|false
458
     */
459
    public function getPermalink(): ?string
460
    {
461
        if (empty($this->getVariable('permalink'))) {
462
            $this->setPermalink($this->getPathname());
463
        }
464
465
        return $this->getVariable('permalink');
466
    }
467
468
    /**
469
     * Get frontmatter.
470
     *
471
     * @return string|null
472
     */
473
    public function getFrontmatter(): ?string
474
    {
475
        return $this->frontmatter;
476
    }
477
478
    /**
479
     * Get body.
480
     *
481
     * @return string
482
     */
483
    public function getBody(): string
484
    {
485
        return $this->body;
486
    }
487
488
    /**
489
     * Set HTML.
490
     *
491
     * @param string $html
492
     *
493
     * @return self
494
     */
495
    public function setHtml(string $html): self
496
    {
497
        $this->html = $html;
498
499
        return $this;
500
    }
501
502
    /**
503
     * Get HTML alias.
504
     *
505
     * @return string|null
506
     */
507
    public function getContent(): ?string
508
    {
509
        return $this->html;
510
    }
511
512
    /**
513
     * Set layout.
514
     *
515
     * @param string $layout
516
     *
517
     * @return self
518
     */
519
    public function setLayout(string $layout): self
520
    {
521
        $this->setVariable('layout', $layout);
522
523
        return $this;
524
    }
525
526
    /**
527
     * Get layout.
528
     *
529
     * @return string|false
530
     */
531
    public function getLayout(): ?string
532
    {
533
        return $this->getVariable('layout');
534
    }
535
}
536