Passed
Push — feat/cast-bool ( 0e044f...27f82c )
by Arnaud
03:56
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.225

Importance

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