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