Passed
Push — feat/markdown-highlighter ( 1efac5...98434c )
by Arnaud
13:07 queued 08:45
created

Page::setVariable()   C

Complexity

Conditions 15
Paths 28

Size

Total Lines 64
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 15.8574

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 34
c 1
b 0
f 0
nc 28
nop 2
dl 0
loc 64
ccs 27
cts 32
cp 0.8438
crap 15.8574
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 string Body before conversion. */
54
    protected $body;
55
56
    /** @var array Front matter before conversion. */
57
    protected $fmVariables = [];
58
59
    /** @var string Body after Markdown conversion. */
60
    protected $html;
61
62
    /** @var string */
63
    protected $language = null;
64
65
    /** @var Slugify */
66
    private static $slugifier;
67
68 1
    public function __construct(string $id)
69
    {
70 1
        parent::__construct($id);
71 1
        $this->setVirtual(true);
72 1
        $this->setType(Type::PAGE);
73
        // default variables
74 1
        $this->setVariables([
75 1
            'title'            => 'Page Title',
76 1
            'date'             => new \DateTime(),
77 1
            'updated'          => new \DateTime(),
78
            'weight'           => null,
79
            'filepath'         => null,
80
            'published'        => true,
81 1
            'content_template' => 'page.content.twig',
82
        ]);
83 1
    }
84
85
    /**
86
     * Turns a path (string) into a slug (URI).
87
     */
88 1
    public static function slugify(string $path): string
89
    {
90 1
        if (!self::$slugifier instanceof Slugify) {
91 1
            self::$slugifier = Slugify::create(['regexp' => self::SLUGIFY_PATTERN]);
92
        }
93
94 1
        return self::$slugifier->slugify($path);
95
    }
96
97
    /**
98
     * Creates the ID from the file path.
99
     */
100 1
    public static function createId(SplFileInfo $file): string
101
    {
102 1
        $relativepath = self::slugify(str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePath()));
103 1
        $basename = self::slugify(PrefixSuffix::subPrefix($file->getBasename('.'.$file->getExtension())));
104
        // case of "README" -> index
105 1
        $basename = (string) str_ireplace('readme', 'index', $basename);
106
        // case of section's index: "section/index" -> "section"
107 1
        if (!empty($relativepath) && PrefixSuffix::sub($basename) == 'index') {
108
            // case of a localized section
109 1
            if (PrefixSuffix::hasSuffix($basename)) {
110
                return $relativepath.'.'.PrefixSuffix::getSuffix($basename);
111
            }
112
113 1
            return $relativepath;
114
        }
115
116 1
        return trim(Util::joinPath($relativepath, $basename), '/');
117
    }
118
119
    /**
120
     * Returns the ID of a page without language suffix.
121
     */
122 1
    public function getIdWithoutLang(): string
123
    {
124 1
        return PrefixSuffix::sub($this->getId());
125
    }
126
127
    /**
128
     * Set file.
129
     */
130 1
    public function setFile(SplFileInfo $file): self
131
    {
132 1
        $this->setVirtual(false);
133 1
        $this->file = $file;
134
135
        /*
136
         * File path components
137
         */
138 1
        $fileRelativePath = str_replace(DIRECTORY_SEPARATOR, '/', $this->file->getRelativePath());
139 1
        $fileExtension = $this->file->getExtension();
140 1
        $fileName = $this->file->getBasename('.'.$fileExtension);
141
        // case of "README" -> "index"
142 1
        $fileName = (string) str_ireplace('readme', 'index', $fileName);
143
        // case of "index" = home page
144 1
        if (empty($this->file->getRelativePath()) && PrefixSuffix::sub($fileName) == 'index') {
145 1
            $this->setType(Type::HOMEPAGE);
146
        }
147
        /*
148
         * Set protected variables
149
         */
150 1
        $this->setFolder($fileRelativePath); // ie: "blog"
151 1
        $this->setSlug($fileName); // ie: "post-1"
152 1
        $this->setPath($this->getFolder().'/'.$this->getSlug()); // ie: "blog/post-1"
153
        /*
154
         * Set default variables
155
         */
156 1
        $this->setVariables([
157 1
            'title'    => PrefixSuffix::sub($fileName),
158 1
            'date'     => (new \DateTime())->setTimestamp($this->file->getCTime()),
159 1
            'updated'  => (new \DateTime())->setTimestamp($this->file->getMTime()),
160 1
            'filepath' => $this->file->getRelativePathname(),
161
        ]);
162
        /*
163
         * Set specific variables
164
         */
165
        // is file has a prefix?
166 1
        if (PrefixSuffix::hasPrefix($fileName)) {
167 1
            $prefix = PrefixSuffix::getPrefix($fileName);
168 1
            if ($prefix !== null) {
169
                // prefix is a valid date?
170 1
                if (Util\Date::isDateValid($prefix)) {
171 1
                    $this->setVariable('date', (string) $prefix);
172
                } else {
173
                    // prefix is an integer: used for sorting
174 1
                    $this->setVariable('weight', (int) $prefix);
175
                }
176
            }
177
        }
178
        // is file has a language suffix?
179 1
        if (PrefixSuffix::hasSuffix($fileName)) {
180 1
            $this->setLanguage(PrefixSuffix::getSuffix($fileName));
181
        }
182
        // set reference between page's translations, even if it exist in only one language
183 1
        $this->setVariable('langref', $this->getPath());
184
185 1
        return $this;
186
    }
187
188
    /**
189
     * Returns file real path.
190
     */
191 1
    public function getFilePath(): ?string
192
    {
193 1
        return $this->file->getRealPath() === false ? null : $this->file->getRealPath();
194
    }
195
196
    /**
197
     * Parse file content.
198
     */
199 1
    public function parse(): self
200
    {
201 1
        $parser = new Parser($this->file);
202 1
        $parsed = $parser->parse();
203 1
        $this->frontmatter = $parsed->getFrontmatter();
204 1
        $this->body = $parsed->getBody();
205
206 1
        return $this;
207
    }
208
209
    /**
210
     * Get frontmatter.
211
     */
212 1
    public function getFrontmatter(): ?string
213
    {
214 1
        return $this->frontmatter;
215
    }
216
217
    /**
218
     * Get body as raw.
219
     */
220 1
    public function getBody(): ?string
221
    {
222 1
        return $this->body;
223
    }
224
225
    /**
226
     * Set virtual status.
227
     */
228 1
    public function setVirtual(bool $virtual): self
229
    {
230 1
        $this->virtual = $virtual;
231
232 1
        return $this;
233
    }
234
235
    /**
236
     * Is current page is virtual?
237
     */
238 1
    public function isVirtual(): bool
239
    {
240 1
        return $this->virtual;
241
    }
242
243
    /**
244
     * Set page type.
245
     */
246 1
    public function setType(string $type): self
247
    {
248 1
        $this->type = new Type($type);
249
250 1
        return $this;
251
    }
252
253
    /**
254
     * Get page type.
255
     */
256 1
    public function getType(): string
257
    {
258 1
        return (string) $this->type;
259
    }
260
261
    /**
262
     * Set path without slug.
263
     */
264 1
    public function setFolder(string $folder): self
265
    {
266 1
        $this->folder = self::slugify($folder);
267
268 1
        return $this;
269
    }
270
271
    /**
272
     * Get path without slug.
273
     */
274 1
    public function getFolder(): ?string
275
    {
276 1
        return $this->folder;
277
    }
278
279
    /**
280
     * Set slug.
281
     */
282 1
    public function setSlug(string $slug): self
283
    {
284 1
        if (!$this->slug) {
285 1
            $slug = self::slugify(PrefixSuffix::sub($slug));
286
        }
287
        // force slug and update path
288 1
        if ($this->slug && $this->slug != $slug) {
289 1
            $this->setPath($this->getFolder().'/'.$slug);
290
        }
291 1
        $this->slug = $slug;
292
293 1
        return $this;
294
    }
295
296
    /**
297
     * Get slug.
298
     */
299 1
    public function getSlug(): string
300
    {
301 1
        return $this->slug;
302
    }
303
304
    /**
305
     * Set path.
306
     */
307 1
    public function setPath(string $path): self
308
    {
309 1
        $path = self::slugify(PrefixSuffix::sub($path));
310
311
        // case of homepage
312 1
        if ($path == 'index') {
313 1
            $this->path = '';
314
315 1
            return $this;
316
        }
317
318
        // case of custom sections' index (ie: content/section/index.md)
319 1
        if (substr($path, -6) == '/index') {
320 1
            $path = substr($path, 0, strlen($path) - 6);
321
        }
322 1
        $this->path = $path;
323
324
        // case of root pages
325 1
        $lastslash = strrpos($this->path, '/');
326 1
        if ($lastslash === false) {
327 1
            $this->slug = $this->path;
328
329 1
            return $this;
330
        }
331
332 1
        if (!$this->virtual && $this->getSection() === null) {
333 1
            $this->section = explode('/', $this->path)[0];
334
        }
335 1
        $this->folder = substr($this->path, 0, $lastslash);
336 1
        $this->slug = substr($this->path, -(strlen($this->path) - $lastslash - 1));
337
338 1
        return $this;
339
    }
340
341
    /**
342
     * Get path.
343
     */
344 1
    public function getPath(): ?string
345
    {
346 1
        return $this->path;
347
    }
348
349
    /**
350
     * @see getPath()
351
     */
352
    public function getPathname(): ?string
353
    {
354
        return $this->getPath();
355
    }
356
357
    /**
358
     * Set section.
359
     */
360 1
    public function setSection(string $section): self
361
    {
362 1
        $this->section = $section;
363
364 1
        return $this;
365
    }
366
367
    /**
368
     * Get section.
369
     */
370 1
    public function getSection(): ?string
371
    {
372 1
        return !empty($this->section) ? $this->section : null;
373
    }
374
375
    /**
376
     * Set body as HTML.
377
     */
378 1
    public function setBodyHtml(string $html): self
379
    {
380 1
        $this->html = $html;
381
382 1
        return $this;
383
    }
384
385
    /**
386
     * Get body as HTML.
387
     */
388 1
    public function getBodyHtml(): ?string
389
    {
390 1
        return $this->html;
391
    }
392
393
    /**
394
     * @see getBodyHtml()
395
     */
396 1
    public function getContent(): ?string
397
    {
398 1
        return $this->getBodyHtml();
399
    }
400
401
    /**
402
     * Set language.
403
     */
404 1
    public function setLanguage(string $language = null): self
405
    {
406 1
        $this->language = $language;
407
408 1
        return $this;
409
    }
410
411
    /**
412
     * Get language.
413
     */
414 1
    public function getLanguage(): ?string
415
    {
416 1
        return $this->language;
417
    }
418
419
    /*
420
     * Helpers to set and get variables.
421
     */
422
423
    /**
424
     * Set an array as variables.
425
     *
426
     * @throws RuntimeException
427
     */
428 1
    public function setVariables(array $variables): self
429
    {
430 1
        foreach ($variables as $key => $value) {
431 1
            $this->setVariable($key, $value);
432
        }
433
434 1
        return $this;
435
    }
436
437
    /**
438
     * Get all variables.
439
     */
440 1
    public function getVariables(): array
441
    {
442 1
        return $this->properties;
443
    }
444
445
    /**
446
     * Set a variable.
447
     *
448
     * @param string $name
449
     * @param mixed  $value
450
     *
451
     * @throws RuntimeException
452
     */
453 1
    public function setVariable(string $name, $value): self
454
    {
455
        // cast some strings to boolean
456 1
        $this->filterBool($value);
457 1
        if (is_array($value)) {
458 1
            array_walk_recursive($value, [$this, 'filterBool']);
459
        }
460
        // behavior for specific named variables
461
        switch ($name) {
462
            /**
463
             * date: 2012-10-08.
464
             */
465 1
            case 'date':
466
                try {
467 1
                    $date = Util\Date::dateToDatetime($value);
468
                } catch (\Exception $e) {
469
                    throw new RuntimeException(\sprintf('Expected date format for "date" in "%s" must be "YYYY-MM-DD" instead of "%s"', $this->getId(), (string) $value));
470
                }
471 1
                $this->offsetSet('date', $date);
472 1
                break;
473
            /**
474
             * schedule:
475
             *   publish: 2012-10-08
476
             *   expiry: 2012-10-09.
477
             */
478 1
            case 'schedule':
479 1
                $this->offsetSet('published', false);
480 1
                if (is_array($value)) {
481 1
                    if (array_key_exists('publish', $value) && Util\Date::dateToDatetime($value['publish']) <= Util\Date::dateToDatetime('now')) {
482
                        $this->offsetSet('published', true);
483
                    }
484 1
                    if (array_key_exists('expiry', $value) && Util\Date::dateToDatetime($value['expiry']) >= Util\Date::dateToDatetime('now')) {
485
                        $this->offsetSet('published', true);
486
                    }
487
                }
488 1
                break;
489
            /**
490
             * draft: true.
491
             */
492 1
            case 'draft':
493 1
                if ($value === true) {
494 1
                    $this->offsetSet('published', 0);
495
                }
496 1
                break;
497
            /**
498
             * path: about/about
499
             * slug: about.
500
             */
501 1
            case 'path':
502 1
            case 'slug':
503 1
                $slugify = self::slugify((string) $value);
504 1
                if ($value != $slugify) {
505
                    throw new RuntimeException(\sprintf('"%s" variable should be "%s" (not "%s") in "%s"', $name, $slugify, (string) $value, $this->getId()));
506
                }
507
                /** @see setPath() */
508
                /** @see setSlug() */
509 1
                $method = 'set'.\ucfirst($name);
510 1
                $this->$method($value);
511 1
                break;
512
            default:
513 1
                $this->offsetSet($name, $value);
514
        }
515
516 1
        return $this;
517
    }
518
519
    /**
520
     * Is variable exists?
521
     */
522 1
    public function hasVariable(string $name): bool
523
    {
524 1
        return $this->offsetExists($name);
525
    }
526
527
    /**
528
     * Get a variable.
529
     *
530
     * @return mixed|null
531
     */
532 1
    public function getVariable(string $name)
533
    {
534 1
        if ($this->offsetExists($name)) {
535 1
            return $this->offsetGet($name);
536
        }
537 1
    }
538
539
    /**
540
     * Unset a variable.
541
     */
542 1
    public function unVariable(string $name): self
543
    {
544 1
        if ($this->offsetExists($name)) {
545 1
            $this->offsetUnset($name);
546
        }
547
548 1
        return $this;
549
    }
550
551
    /**
552
     * Set front matter (only) variables.
553
     */
554 1
    public function setFmVariables(array $variables): self
555
    {
556 1
        $this->fmVariables = $variables;
557
558 1
        return $this;
559
    }
560
561
    /**
562
     * Get front matter variables.
563
     */
564 1
    public function getFmVariables(): array
565
    {
566 1
        return $this->fmVariables;
567
    }
568
569
    /**
570
     * Filter 'true', 'false', 'on', 'off', 'yes', 'no' to boolean.
571
     */
572 1
    private function filterBool(&$value)
573
    {
574 1
        if (is_string($value)) {
575 1
            if (in_array($value, ['true', 'on', 'yes'])) {
576
                $value = true;
577
            }
578 1
            if (in_array($value, ['false', 'off', 'no'])) {
579
                $value = false;
580
            }
581
        }
582 1
    }
583
}
584