Passed
Push — configuration ( efba55...1fb2fd )
by Arnaud
15:16 queued 10:46
created

Page::setVariable()   C

Complexity

Conditions 17
Paths 18

Size

Total Lines 50
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 17.8433

Importance

Changes 0
Metric Value
cc 17
eloc 34
c 0
b 0
f 0
nc 18
nop 2
dl 0
loc 50
ccs 24
cts 28
cp 0.8571
crap 17.8433
rs 5.2166

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
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Cecil\Collection\Page;
15
16
use Cecil\Collection\Item;
17
use Cecil\Exception\RuntimeException;
18
use Cecil\Util;
19
use Cocur\Slugify\Slugify;
20
use Symfony\Component\Finder\SplFileInfo;
21
22
/**
23
 * Class Page.
24
 */
25
class Page extends Item
26
{
27
    public const SLUGIFY_PATTERN = '/(^\/|[^._a-z0-9\/]|-)+/'; // should be '/^\/|[^_a-z0-9\/]+/'
28
29
    /** @var bool True if page is not created from a file. */
30
    protected $virtual;
31
32
    /** @var SplFileInfo */
33
    protected $file;
34
35
    /** @var Type Type */
36
    protected $type;
37
38
    /** @var string */
39
    protected $folder;
40
41
    /** @var string */
42
    protected $slug;
43
44
    /** @var string path = folder + slug. */
45
    protected $path;
46
47
    /** @var string */
48
    protected $section;
49
50
    /** @var string */
51
    protected $frontmatter;
52
53
    /** @var array Front matter before conversion. */
54
    protected $fmVariables = [];
55
56
    /** @var string Body before conversion. */
57
    protected $body;
58
59
    /** @var string Body after conversion. */
60
    protected $html;
61
62
    /** @var array Output, by format */
63
    protected $rendered = [];
64
65
    /** @var Collection Subpages of a list page. */
66
    protected $subPages;
67
68
    /** @var array */
69
    protected $paginator = [];
70
71
    /** @var \Cecil\Collection\Taxonomy\Vocabulary Terms of a vocabulary. */
72
    protected $terms;
73
74
    /** @var Slugify */
75
    private static $slugifier;
76
77 1
    public function __construct(string $id)
78
    {
79 1
        parent::__construct($id);
80 1
        $this->setVirtual(true);
81 1
        $this->setType(Type::PAGE->value);
82
        // default variables
83 1
        $this->setVariables([
84 1
            'title'            => 'Page Title',
85 1
            'date'             => new \DateTime(),
86 1
            'updated'          => new \DateTime(),
87 1
            'weight'           => null,
88 1
            'filepath'         => null,
89 1
            'published'        => true,
90 1
            'content_template' => 'page.content.twig',
91 1
        ]);
92
    }
93
94
    /**
95
     * toString magic method to prevent Twig get_attribute fatal error.
96
     *
97
     * @return string
98
     */
99 1
    public function __toString()
100
    {
101 1
        return $this->getId();
102
    }
103
104
    /**
105
     * Turns a path (string) into a slug (URI).
106
     */
107 1
    public static function slugify(string $path): string
108
    {
109 1
        if (!self::$slugifier instanceof Slugify) {
110 1
            self::$slugifier = Slugify::create(['regexp' => self::SLUGIFY_PATTERN]);
111
        }
112
113 1
        return self::$slugifier->slugify($path);
114
    }
115
116
    /**
117
     * Creates the ID from the file (path).
118
     */
119 1
    public static function createIdFromFile(SplFileInfo $file): string
120
    {
121 1
        $relativePath = self::slugify(str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePath()));
122 1
        $basename = self::slugify(PrefixSuffix::subPrefix($file->getBasename('.' . $file->getExtension())));
123
        // if file is "README.md", ID is "index"
124 1
        $basename = (string) str_ireplace('readme', 'index', $basename);
125
        // if file is section's index: "section/index.md", ID is "section"
126 1
        if (!empty($relativePath) && PrefixSuffix::sub($basename) == 'index') {
127
            // case of a localized section's index: "section/index.fr.md", ID is "fr/section"
128 1
            if (PrefixSuffix::hasSuffix($basename)) {
129 1
                return PrefixSuffix::getSuffix($basename) . '/' . $relativePath;
130
            }
131
132 1
            return $relativePath;
133
        }
134
        // localized page
135 1
        if (PrefixSuffix::hasSuffix($basename)) {
136 1
            return trim(Util::joinPath(PrefixSuffix::getSuffix($basename), $relativePath, PrefixSuffix::sub($basename)), '/');
137
        }
138
139 1
        return trim(Util::joinPath($relativePath, $basename), '/');
140
    }
141
142
    /**
143
     * Returns the ID of a page without language.
144
     */
145 1
    public function getIdWithoutLang(): string
146
    {
147 1
        $langPrefix = $this->getVariable('language') . '/';
148 1
        if ($this->hasVariable('language') && Util\Str::startsWith($this->getId(), $langPrefix)) {
149 1
            return substr($this->getId(), \strlen($langPrefix));
150
        }
151
152 1
        return $this->getId();
153
    }
154
155
    /**
156
     * Set file.
157
     */
158 1
    public function setFile(SplFileInfo $file): self
159
    {
160 1
        $this->file = $file;
161 1
        $this->setVirtual(false);
162
163
        /*
164
         * File path components
165
         */
166 1
        $fileRelativePath = str_replace(DIRECTORY_SEPARATOR, '/', $this->file->getRelativePath());
167 1
        $fileExtension = $this->file->getExtension();
168 1
        $fileName = $this->file->getBasename('.' . $fileExtension);
169
        // renames "README" to "index"
170 1
        $fileName = (string) str_ireplace('readme', 'index', $fileName);
171
        // case of "index" = home page
172 1
        if (empty($this->file->getRelativePath()) && PrefixSuffix::sub($fileName) == 'index') {
173 1
            $this->setType(Type::HOMEPAGE->value);
174
        }
175
        /*
176
         * Set page properties and variables
177
         */
178 1
        $this->setFolder($fileRelativePath);
179 1
        $this->setSlug($fileName);
180 1
        $this->setPath($this->getFolder() . '/' . $this->getSlug());
181 1
        $this->setVariables([
182 1
            'title'    => PrefixSuffix::sub($fileName),
183 1
            'date'     => (new \DateTime())->setTimestamp($this->file->getMTime()),
184 1
            'updated'  => (new \DateTime())->setTimestamp($this->file->getMTime()),
185 1
            'filepath' => $this->file->getRelativePathname(),
186 1
        ]);
187
        /*
188
         * Set specific variables
189
         */
190
        // is file has a prefix?
191 1
        if (PrefixSuffix::hasPrefix($fileName)) {
192 1
            $prefix = PrefixSuffix::getPrefix($fileName);
193 1
            if ($prefix !== null) {
194
                // prefix is a valid date?
195 1
                if (Util\Date::isValid($prefix)) {
196 1
                    $this->setVariable('date', (string) $prefix);
197
                } else {
198
                    // prefix is an integer: used for sorting
199 1
                    $this->setVariable('weight', (int) $prefix);
200
                }
201
            }
202
        }
203
        // is file has a language suffix?
204 1
        if (PrefixSuffix::hasSuffix($fileName)) {
205 1
            $this->setVariable('language', PrefixSuffix::getSuffix($fileName));
206
        }
207
        // set reference between page's translations, even if it exist in only one language
208 1
        $this->setVariable('langref', $this->getPath());
209
210 1
        return $this;
211
    }
212
213
    /**
214
     * Returns file name, with extension.
215
     */
216 1
    public function getFileName(): string
217
    {
218 1
        return $this->file->getBasename();
219
    }
220
221
    /**
222 1
     * Returns file real path.
223
     */
224
    public function getFilePath(): ?string
225
    {
226
        if ($this->file === null) {
227
            return null;
228 1
        }
229
230 1
        return $this->file->getRealPath() === false ? null : $this->file->getRealPath();
231 1
    }
232 1
233 1
    /**
234
     * Parse file content.
235 1
     */
236
    public function parse(): self
237
    {
238
        $parser = new Parser($this->file);
239
        $parsed = $parser->parse();
240
        $this->frontmatter = $parsed->getFrontmatter();
241 1
        $this->body = $parsed->getBody();
242
243 1
        return $this;
244
    }
245
246
    /**
247
     * Get front matter.
248
     */
249 1
    public function getFrontmatter(): ?string
250
    {
251 1
        return $this->frontmatter;
252
    }
253
254
    /**
255
     * Get body as raw.
256
     */
257 1
    public function getBody(): ?string
258
    {
259 1
        return $this->body;
260
    }
261 1
262
    /**
263
     * Set virtual status.
264
     */
265
    public function setVirtual(bool $virtual): self
266
    {
267 1
        $this->virtual = $virtual;
268
269 1
        return $this;
270
    }
271
272
    /**
273
     * Is current page is virtual?
274
     */
275 1
    public function isVirtual(): bool
276
    {
277 1
        return $this->virtual;
278
    }
279 1
280
    /**
281
     * Set page type.
282
     */
283
    public function setType(string $type): self
284
    {
285 1
        $this->type = Type::from($type);
286
287 1
        return $this;
288
    }
289
290
    /**
291
     * Get page type.
292
     */
293 1
    public function getType(): string
294
    {
295 1
        return $this->type->value;
296
    }
297 1
298
    /**
299
     * Set path without slug.
300
     */
301
    public function setFolder(string $folder): self
302
    {
303 1
        $this->folder = self::slugify($folder);
304
305 1
        return $this;
306
    }
307
308
    /**
309
     * Get path without slug.
310
     */
311 1
    public function getFolder(): ?string
312
    {
313 1
        return $this->folder;
314 1
    }
315
316
    /**
317 1
     * Set slug.
318 1
     */
319
    public function setSlug(string $slug): self
320 1
    {
321
        if (!$this->slug) {
322 1
            $slug = self::slugify(PrefixSuffix::sub($slug));
323
        }
324
        // force slug and update path
325
        if ($this->slug && $this->slug != $slug) {
326
            $this->setPath($this->getFolder() . '/' . $slug);
327
        }
328 1
        $this->slug = $slug;
329
330 1
        return $this;
331
    }
332
333
    /**
334
     * Get slug.
335
     */
336 1
    public function getSlug(): string
337
    {
338 1
        return $this->slug;
339
    }
340
341 1
    /**
342 1
     * Set path.
343
     */
344 1
    public function setPath(string $path): self
345
    {
346
        $path = trim($path, '/');
347
348 1
        // case of homepage
349 1
        if ($path == 'index') {
350
            $this->path = '';
351 1
352
            return $this;
353 1
        }
354
355
        // case of custom sections' index (ie: section/index.md -> section)
356 1
        if (substr($path, -6) == '/index') {
357 1
            $path = substr($path, 0, \strlen($path) - 6);
358
        }
359 1
        $this->path = $path;
360
361
        $lastslash = strrpos($this->path, '/');
362
363 1
        // case of root/top-level pages
364 1
        if ($lastslash === false) {
365
            $this->slug = $this->path;
366
367 1
            return $this;
368 1
        }
369
370 1
        // case of sections' pages: set section
371
        if (!$this->virtual && $this->getSection() === null) {
372
            $this->section = explode('/', $this->path)[0];
373
        }
374
        // set/update folder and slug
375
        $this->folder = substr($this->path, 0, $lastslash);
376 1
        $this->slug = substr($this->path, -(\strlen($this->path) - $lastslash - 1));
377
378 1
        return $this;
379
    }
380
381
    /**
382
     * Get path.
383
     */
384
    public function getPath(): ?string
385
    {
386
        return $this->path;
387
    }
388
389
    /**
390
     * @see getPath()
391
     */
392 1
    public function getPathname(): ?string
393
    {
394 1
        return $this->getPath();
395
    }
396 1
397
    /**
398
     * Set section.
399
     */
400
    public function setSection(string $section): self
401
    {
402 1
        $this->section = $section;
403
404 1
        return $this;
405
    }
406
407
    /**
408
     * Get section.
409
     */
410
    public function getSection(): ?string
411
    {
412
        return !empty($this->section) ? $this->section : null;
413
    }
414
415
    /**
416
     * Unset section.
417
     */
418
    public function unSection(): self
419
    {
420 1
        $this->section = null;
421
422 1
        return $this;
423
    }
424 1
425
    /**
426
     * Set body as HTML.
427
     */
428
    public function setBodyHtml(string $html): self
429
    {
430 1
        $this->html = $html;
431
432 1
        return $this;
433
    }
434
435
    /**
436
     * Get body as HTML.
437
     */
438 1
    public function getBodyHtml(): ?string
439
    {
440 1
        return $this->html;
441
    }
442
443
    /**
444
     * @see getBodyHtml()
445
     */
446 1
    public function getContent(): ?string
447
    {
448 1
        return $this->getBodyHtml();
449
    }
450 1
451
    /**
452
     * Add rendered.
453
     */
454
    public function addRendered(array $rendered): self
455
    {
456 1
        $this->rendered += $rendered;
457
458 1
        return $this;
459
    }
460
461
    /**
462
     * Get rendered.
463
     */
464 1
    public function getRendered(): array
465
    {
466 1
        return $this->rendered;
467
    }
468 1
469
    /**
470
     * Set Subpages.
471
     */
472
    public function setPages(\Cecil\Collection\Page\Collection $subPages): self
473
    {
474 1
        $this->subPages = $subPages;
475
476 1
        return $this;
477
    }
478
479
    /**
480
     * Get Subpages.
481
     */
482 1
    public function getPages(): ?\Cecil\Collection\Page\Collection
483
    {
484 1
        return $this->subPages;
485
    }
486 1
487
    /**
488
     * Set paginator.
489
     */
490
    public function setPaginator(array $paginator): self
491
    {
492 1
        $this->paginator = $paginator;
493
494 1
        return $this;
495
    }
496
497
    /**
498
     * Get paginator.
499
     */
500
    public function getPaginator(): array
501
    {
502
        return $this->paginator;
503
    }
504
505
    /**
506
     * Paginator backward compatibility.
507
     */
508 1
    public function getPagination(): array
509
    {
510 1
        return $this->getPaginator();
511
    }
512 1
513
    /**
514
     * Set vocabulary terms.
515
     */
516
    public function setTerms(\Cecil\Collection\Taxonomy\Vocabulary $terms): self
517
    {
518 1
        $this->terms = $terms;
519
520 1
        return $this;
521
    }
522
523
    /**
524
     * Get vocabulary terms.
525
     */
526
    public function getTerms(): \Cecil\Collection\Taxonomy\Vocabulary
527
    {
528
        return $this->terms;
529
    }
530
531
    /*
532 1
     * Helpers to set and get variables.
533
     */
534 1
535 1
    /**
536
     * Set an array as variables.
537
     *
538 1
     * @throws RuntimeException
539
     */
540
    public function setVariables(array $variables): self
541
    {
542
        foreach ($variables as $key => $value) {
543
            $this->setVariable($key, $value);
544 1
        }
545
546 1
        return $this;
547
    }
548
549
    /**
550
     * Get all variables.
551
     */
552
    public function getVariables(): array
553
    {
554
        return $this->properties;
555
    }
556
557 1
    /**
558
     * Set a variable.
559 1
     *
560
     * @param string $name  Name of the variable
561 1
     * @param mixed  $value Value of the variable
562 1
     *
563 1
     * @throws RuntimeException
564
     */
565 1
    public function setVariable(string $name, $value): self
566
    {
567
        $this->filterBool($value);
568
        switch ($name) {
569 1
            case 'date':
570 1
            case 'updated':
571
            case 'lastmod':
572 1
                try {
573
                    $date = Util\Date::toDatetime($value);
574
                } catch (\Exception) {
575
                    throw new \Exception(\sprintf('The value of "%s" is not a valid date: "%s".', $name, var_export($value, true)));
576
                }
577 1
                $this->offsetSet($name == 'lastmod' ? 'updated' : $name, $date);
578 1
                break;
579 1
580 1
            case 'schedule':
581
                /*
582 1
                 * publish: 2012-10-08
583
                 * expiry: 2012-10-09
584
                 */
585
                $this->offsetSet('published', false);
586 1
                if (\is_array($value)) {
587 1
                    if (\array_key_exists('publish', $value) && Util\Date::toDatetime($value['publish']) <= Util\Date::toDatetime('now')) {
588
                        $this->offsetSet('published', true);
589 1
                    }
590 1
                    if (\array_key_exists('expiry', $value) && Util\Date::toDatetime($value['expiry']) >= Util\Date::toDatetime('now')) {
591
                        $this->offsetSet('published', true);
592 1
                    }
593 1
                }
594 1
                break;
595 1
            case 'draft':
596 1
                // draft: true = published: false
597
                if ($value === true) {
598
                    $this->offsetSet('published', false);
599 1
                }
600 1
                break;
601 1
            case 'path':
602
            case 'slug':
603 1
                $slugify = self::slugify((string) $value);
604
                if ($value != $slugify) {
605
                    throw new RuntimeException(\sprintf('"%s" variable should be "%s" (not "%s") in "%s".', $name, $slugify, (string) $value, $this->getId()));
606 1
                }
607
                $method = 'set' . ucfirst($name);
608
                $this->$method($value);
609
                break;
610
            default:
611
                $this->offsetSet($name, $value);
612
        }
613
614 1
        return $this;
615
    }
616 1
617
    /**
618
     * Is variable exists?
619
     *
620
     * @param string $name Name of the variable
621
     */
622
    public function hasVariable(string $name): bool
623
    {
624
        return $this->offsetExists($name);
625
    }
626
627 1
    /**
628
     * Get a variable.
629 1
     *
630 1
     * @param string     $name    Name of the variable
631
     * @param mixed|null $default Default value
632
     *
633 1
     * @return mixed|null
634
     */
635
    public function getVariable(string $name, $default = null)
636
    {
637
        if ($this->offsetExists($name)) {
638
            return $this->offsetGet($name);
639
        }
640
641 1
        return $default;
642
    }
643 1
644 1
    /**
645
     * Unset a variable.
646
     *
647 1
     * @param string $name Name of the variable
648
     */
649
    public function unVariable(string $name): self
650
    {
651
        if ($this->offsetExists($name)) {
652
            $this->offsetUnset($name);
653 1
        }
654
655 1
        return $this;
656
    }
657 1
658
    /**
659
     * Set front matter (only) variables.
660
     */
661
    public function setFmVariables(array $variables): self
662
    {
663 1
        $this->fmVariables = $variables;
664
665 1
        return $this;
666
    }
667
668
    /**
669
     * Get front matter variables.
670
     */
671
    public function getFmVariables(): array
672
    {
673
        return $this->fmVariables;
674
    }
675
676
    /**
677 1
     * Cast "boolean" string (or array of strings) to boolean.
678
     *
679 1
     * @param mixed $value Value to filter
680 1
     *
681 1
     * @return bool|mixed
682
     *
683
     * @see strToBool()
684
     */
685
    private function filterBool(&$value)
686
    {
687
        \Cecil\Util\Str::strToBool($value);
688 1
        if (\is_array($value)) {
689
            array_walk_recursive($value, '\Cecil\Util\Str::strToBool');
690 1
        }
691
    }
692
693
    /**
694
     * {@inheritdoc}
695
     */
696
    public function setId(string $id): self
697
    {
698
        return parent::setId($id);
699
    }
700
}
701