Passed
Pull Request — master (#1704)
by Arnaud
08:34 queued 03:24
created

Page::setVariable()   C

Complexity

Conditions 15
Paths 16

Size

Total Lines 49
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 15.483

Importance

Changes 0
Metric Value
cc 15
eloc 33
c 0
b 0
f 0
nc 16
nop 2
dl 0
loc 49
ccs 27
cts 31
cp 0.871
crap 15.483
rs 5.9166

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