Completed
Push — feature-output-formats ( 71c0b9...769ef8 )
by Arnaud
09:01
created

Page::getVariables()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * Copyright (c) Arnaud Ligny <[email protected]>
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Cecil\Collection\Page;
10
11
use Cecil\Collection\Item;
12
use Cecil\Page\Parser;
13
use Cecil\Page\Prefix;
14
use Cecil\Page\Type;
15
use Cecil\Page\VariableTrait;
16
use Cecil\Util;
17
use Cocur\Slugify\Slugify;
18
use Symfony\Component\Finder\SplFileInfo;
19
20
/**
21
 * Class Page.
22
 */
23
class Page extends Item
24
{
25
    const SLUGIFY_PATTERN = '/(^\/|[^a-z0-9\/]|-)+/';
26
27
    /**
28
     * @var SplFileInfo
29
     */
30
    protected $file;
31
    /**
32
     * @var string
33
     */
34
    protected $fileExtension;
35
    /**
36
     * @var string
37
     */
38
    protected $filePath;
39
    /**
40
     * @var string
41
     */
42
    protected $fileName;
43
    /**
44
     * @var string
45
     */
46
    protected $filePathname;
47
    /**
48
     * @var bool
49
     */
50
    protected $virtual = false;
51
    /**
52
     * @var string
53
     */
54
    protected $type;
55
    /**
56
     * @var string
57
     */
58
    protected $pathname;
59
    /**
60
     * @var string
61
     */
62
    protected $path;
63
    /**
64
     * @var string
65
     */
66
    protected $name;
67
    /**
68
     * @var string
69
     */
70
    protected $section;
71
    /**
72
     * @var string
73
     */
74
    protected $frontmatter;
75
    /**
76
     * @var string
77
     */
78
    protected $body;
79
    /**
80
     * @var string
81
     */
82
    protected $html;
83
84
    /**
85
     * Constructor.
86
     *
87
     * @param SplFileInfo|null $file
88
     */
89
    public function __construct(SplFileInfo $file = null)
90
    {
91
        $this->file = $file;
92
93
        // physical page
94
        if ($this->file instanceof SplFileInfo) {
95
            /*
96
             * File path components
97
             */
98
            // ie: content/Blog/Post 1.md
99
            //             |    |      └─ fileExtension
100
            //             |    └─ fileName
101
            //             └─ filePath
102
            $this->fileExtension = pathinfo($this->file, PATHINFO_EXTENSION);
103
            $this->filePath = str_replace(DIRECTORY_SEPARATOR, '/', $this->file->getRelativePath());
104
            $this->fileName = $this->file->getBasename('.'.$this->fileExtension);
105
            // filePathname = filePath + '/' + fileName
106
            // ie: content/Blog/Post 1.md -> "Blog/Post 1"
107
            // ie: content/index.md -> "index"
108
            // ie: content/Blog/index.md -> "Blog/"
109
            $this->filePathname = ($this->filePath ? $this->filePath.'/' : '')
110
                .($this->filePath && $this->fileName == 'index' ? '' : $this->fileName);
111
            /*
112
             * Set properties
113
             */
114
            // ID. ie: "blog/post-1"
115
            $this->id = $this->slugify(Prefix::subPrefix($this->filePathname));
116
            // Path. ie: "blog"
117
            $this->path = $this->slugify($this->filePath);
118
            // Name. ie: "post-1"
119
            $this->name = $this->slugify(Prefix::subPrefix($this->fileName));
120
            // Pathname. ie: "blog/post-1"
121
            $this->pathname = $this->slugify(Prefix::subPrefix($this->filePathname));
122
            /*
123
             * Set variables
124
             */
125
            // Section. ie: "blog"
126
            $this->setSection(explode('/', $this->path)[0]);
127
            /*
128
             * Set variables overridden by front matter
129
             */
130
            // title. ie: "Post 1"
131
            $this->setVariable('title', Prefix::subPrefix($this->fileName));
132
            // date (from file meta)
133
            $this->setVariable('date', filemtime($this->file->getPathname()));
134
            // weight
135
            $this->setVariable('weight', null);
136
            // special case: file has a prefix
137
            if (Prefix::hasPrefix($this->filePathname)) {
138
                // prefix is a valid date?
139
                if (Util::isValidDate(Prefix::getPrefix($this->filePathname))) {
140
                    $this->setVariable('date', (string) Prefix::getPrefix($this->filePathname));
141
                } else {
142
                    // prefix is an integer, use for sorting
143
                    $this->setVariable('weight', (int) Prefix::getPrefix($this->filePathname));
144
                }
145
            }
146
147
            parent::__construct($this->id);
148
        } else {
149
            // virtual page
150
            $this->virtual = true;
151
            // default variables
152
            $this->setVariables([
153
                'title'  => 'Default Title',
154
                'date'   => time(),
155
                'weight' => null,
156
            ]);
157
158
            parent::__construct();
159
        }
160
        $this->setType(Type::PAGE);
161
        // required variables
162
        $this->setVariable('virtual', $this->virtual);
163
        $this->setVariable('published', true);
164
        $this->setVariable('content_template', 'page.content.twig');
165
    }
166
167
    /**
168
     * Turn a path (string) into a slung (URL).
169
     *
170
     * @param string $string
171
     *
172
     * @return string
173
     */
174
    public static function slugify(string $string): string
175
    {
176
        return Slugify::create([
177
            'regexp' => self::SLUGIFY_PATTERN,
178
        ])->slugify($string);
179
    }
180
181
    /**
182
     * Is current page is virtual?
183
     *
184
     * @return bool
185
     */
186
    public function isVirtual(): bool
187
    {
188
        return $this->virtual;
189
    }
190
191
    /**
192
     * Set page type.
193
     *
194
     * @param string $type
195
     *
196
     * @return self
197
     */
198
    public function setType(string $type): self
199
    {
200
        $this->type = new Type($type);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Cecil\Page\Type($type) of type object<Cecil\Page\Type> is incompatible with the declared type string of property $type.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
201
202
        return $this;
203
    }
204
205
    /**
206
     * Get page type.
207
     *
208
     * @return string|null
209
     */
210
    public function getType(): ?string
211
    {
212
        return $this->type;
213
    }
214
215
    /**
216
     * Parse file content.
217
     *
218
     * @return self
219
     */
220
    public function parse(): self
221
    {
222
        $parser = new Parser($this->file);
223
        $parsed = $parser->parse();
224
        $this->frontmatter = $parsed->getFrontmatter();
225
        $this->body = $parsed->getBody();
226
227
        return $this;
228
    }
229
230
    /**
231
     * Set name.
232
     *
233
     * @param string $name
234
     *
235
     * @return self
236
     */
237
    public function setName(string $name): self
238
    {
239
        $this->name = $name;
240
241
        return $this;
242
    }
243
244
    /**
245
     * Get name.
246
     *
247
     * @return string|null
248
     */
249
    public function getName(): ?string
250
    {
251
        return $this->name;
252
    }
253
254
    /**
255
     * Set path.
256
     *
257
     * @param $path
258
     *
259
     * @return self
260
     */
261
    public function setPath(string $path): self
262
    {
263
        $this->path = $path;
264
265
        return $this;
266
    }
267
268
    /**
269
     * Get path.
270
     *
271
     * @return string
272
     */
273
    public function getPath(): string
274
    {
275
        return $this->path;
276
    }
277
278
    /**
279
     * Set path name.
280
     *
281
     * @param string $pathname
282
     *
283
     * @return self
284
     */
285
    public function setPathname(string $pathname): self
286
    {
287
        $this->pathname = $pathname;
288
289
        return $this;
290
    }
291
292
    /**
293
     * Get path name.
294
     *
295
     * @return string
296
     */
297
    public function getPathname(): string
298
    {
299
        if ($this->hasVariable('url')
300
            && $this->pathname != $this->getVariable('url')
301
        ) {
302
            $this->setPathname($this->getVariable('url'));
303
        }
304
305
        return $this->pathname;
306
    }
307
308
    /**
309
     * Set section.
310
     *
311
     * @param string $section
312
     *
313
     * @return self
314
     */
315
    public function setSection(string $section): self
316
    {
317
        $this->section = $section;
318
319
        return $this;
320
    }
321
322
    /**
323
     * Get section.
324
     *
325
     * @return string|false
326
     */
327
    public function getSection(): ?string
328
    {
329
        if (empty($this->section) && !empty($this->path)) {
330
            $this->setSection(explode('/', $this->path)[0]);
331
        }
332
333
        return $this->section;
334
    }
335
336
    /**
337
     * Get frontmatter.
338
     *
339
     * @return string|null
340
     */
341
    public function getFrontmatter(): ?string
342
    {
343
        return $this->frontmatter;
344
    }
345
346
    /**
347
     * Get body as raw.
348
     *
349
     * @return string
350
     */
351
    public function getBody(): ?string
352
    {
353
        return $this->body;
354
    }
355
356
    /**
357
     * Set body as HTML.
358
     *
359
     * @param string $html
360
     *
361
     * @return self
362
     */
363
    public function setBodyHtml(string $html): self
364
    {
365
        $this->html = $html;
366
367
        return $this;
368
    }
369
370
    /**
371
     * Get body as HTML.
372
     *
373
     * @return string|null
374
     */
375
    public function getBodyHtml(): ?string
376
    {
377
        return $this->html;
378
    }
379
380
    /**
381
     * @see getBodyHtml()
382
     *
383
     * @return string|null
384
     */
385
    public function getContent(): ?string
386
    {
387
        return $this->getBodyHtml();
388
    }
389
390
    /*
391
     * Helper to set and get variables.
392
     */
393
394
    /**
395
     * Set an array as variables.
396
     *
397
     * @param array $variables
398
     *
399
     * @throws \Exception
400
     *
401
     * @return $this
402
     */
403
    public function setVariables($variables)
404
    {
405
        if (!is_array($variables)) {
406
            throw new \Exception('Can\'t set variables: parameter is not an array');
407
        }
408
        foreach ($variables as $key => $value) {
409
            $this->setVariable($key, $value);
410
        }
411
412
        return $this;
413
    }
414
415
    /**
416
     * Get all variables.
417
     *
418
     * @return array
419
     */
420
    public function getVariables()
421
    {
422
        return $this->properties;
423
    }
424
425
    /**
426
     * Set a variable.
427
     *
428
     * @param $name
429
     * @param $value
430
     *
431
     * @throws \Exception
432
     *
433
     * @return $this
434
     */
435
    public function setVariable($name, $value)
436
    {
437
        switch ($name) {
438
            case 'date':
439
                try {
440
                    if ($value instanceof \DateTime) {
441
                        $date = $value;
442
                    } else {
443
                        // timestamp
444
                        if (is_numeric($value)) {
445
                            $date = (new \DateTime())->setTimestamp($value);
446
                        } else {
447
                            // ie: 2019-01-01
448
                            if (is_string($value)) {
449
                                $date = new \DateTime($value);
450
                            }
451
                        }
452
                    }
453
                } catch (\Exception $e) {
454
                    throw new \Exception(sprintf("Expected date string in page '%s'", $this->getId()));
455
                }
456
                $this->offsetSet('date', $date);
0 ignored issues
show
Bug introduced by
The variable $date does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
457
                break;
458
            case 'draft':
459
                if ($value === true) {
460
                    $this->offsetSet('published', false);
461
                }
462
                break;
463
            case 'url':
464
                $slug = self::slugify($value);
465
                if ($value != $slug) {
466
                    throw new \Exception(sprintf("'url' variable should be '%s', not '%s', in page '%s'", $slug, $value, $this->getId()));
467
                }
468
                break;
469
            default:
470
                $this->offsetSet($name, $value);
471
        }
472
473
        return $this;
474
    }
475
476
    /**
477
     * Is variable exist?
478
     *
479
     * @param $name
480
     *
481
     * @return bool
482
     */
483
    public function hasVariable($name)
484
    {
485
        return $this->offsetExists($name);
486
    }
487
488
    /**
489
     * Get a variable.
490
     *
491
     * @param string $name
492
     *
493
     * @return mixed|null
494
     */
495
    public function getVariable($name)
496
    {
497
        if ($this->offsetExists($name)) {
498
            return $this->offsetGet($name);
499
        }
500
    }
501
502
    /**
503
     * Unset a variable.
504
     *
505
     * @param $name
506
     *
507
     * @return $this
508
     */
509
    public function unVariable($name)
510
    {
511
        if ($this->offsetExists($name)) {
512
            $this->offsetUnset($name);
513
        }
514
515
        return $this;
516
    }
517
}
518