Passed
Push — refactor ( 3f0c95...21c0c1 )
by Arnaud
14:16
created

Page::setVariable()   C

Complexity

Conditions 15
Paths 16

Size

Total Lines 65
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 15.9442

Importance

Changes 4
Bugs 2 Features 0
Metric Value
cc 15
eloc 33
c 4
b 2
f 0
nc 16
nop 2
dl 0
loc 65
ccs 26
cts 31
cp 0.8387
crap 15.9442
rs 5.9166

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