Passed
Pull Request — master (#1698)
by Arnaud
12:18 queued 06:33
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 Markdown 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 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 Markdown conversion. */
60
    protected $html;
61
62
    /** @var array Output by format */
63
    protected $rendered = [];
64
65
    /** @var \Cecil\Collection\Page\Collection Subpages of a section */
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);
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
     * Turns a path (string) into a slug (URI).
96
     */
97 1
    public static function slugify(string $path): string
98
    {
99 1
        if (!self::$slugifier instanceof Slugify) {
100 1
            self::$slugifier = Slugify::create(['regexp' => self::SLUGIFY_PATTERN]);
101
        }
102
103 1
        return self::$slugifier->slugify($path);
104
    }
105
106
    /**
107
     * Creates the ID from the file path.
108
     */
109 1
    public static function createIdFromFile(SplFileInfo $file): string
110
    {
111 1
        $relativePath = self::slugify(str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePath()));
112 1
        $basename = self::slugify(PrefixSuffix::subPrefix($file->getBasename('.' . $file->getExtension())));
113
        // if file is "README.md", ID is "index"
114 1
        $basename = (string) str_ireplace('readme', 'index', $basename);
115
        // if file is section's index: "section/index.md", ID is "section"
116 1
        if (!empty($relativePath) && PrefixSuffix::sub($basename) == 'index') {
117
            // case of a localized section's index: "section/index.fr.md", ID is "fr/section"
118 1
            if (PrefixSuffix::hasSuffix($basename)) {
119 1
                return PrefixSuffix::getSuffix($basename) . '/' . $relativePath;
120
            }
121
122 1
            return $relativePath;
123
        }
124
        // localized page
125 1
        if (PrefixSuffix::hasSuffix($basename)) {
126 1
            return trim(Util::joinPath(PrefixSuffix::getSuffix($basename), $relativePath, PrefixSuffix::sub($basename)), '/');
1 ignored issue
show
Bug introduced by
It seems like Cecil\Collection\Page\Pr...x::getSuffix($basename) can also be of type null; however, parameter $path of Cecil\Util::joinPath() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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