Passed
Push — page_variables ( c2adb9...f2aa24 )
by Arnaud
03:11
created

Page::setVariable()   C

Complexity

Conditions 14
Paths 14

Size

Total Lines 64
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 14.0245

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 14
eloc 32
c 3
b 1
f 0
nc 14
nop 2
dl 0
loc 64
ccs 19
cts 20
cp 0.95
crap 14.0245
rs 6.2666

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