Completed
Push — getenv ( d73c36 )
by Arnaud
04:05
created

Extension::getEnv()   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 1
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\Renderer\Twig;
10
11
use Cecil\Collection\Collection;
12
use Cecil\Collection\CollectionInterface;
13
use Cecil\Collection\Page\Page;
14
use Cecil\Exception\Exception;
15
use Cocur\Slugify\Bridge\Twig\SlugifyExtension;
16
use Cocur\Slugify\Slugify;
17
use Leafo\ScssPhp\Compiler;
18
use MatthiasMullie\Minify;
19
use Symfony\Component\Filesystem\Filesystem;
20
21
/**
22
 * Class Twig\Extension.
23
 */
24
class Extension extends SlugifyExtension
25
{
26
    /* @var string */
27
    protected $destPath;
28
    /**
29
     * @var Filesystem
30
     */
31
    protected $fileSystem;
32
33
    /**
34
     * Constructor.
35
     *
36
     * @param string $destPath
37
     */
38
    public function __construct($destPath)
39
    {
40
        $this->destPath = $destPath;
41
        parent::__construct(Slugify::create([
42
            'regexp' => Page::SLUGIFY_PATTERN,
43
        ]));
44
45
        $this->fileSystem = new Filesystem();
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function getName()
52
    {
53
        return 'cecil';
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function getFilters()
60
    {
61
        return [
62
            new \Twig_SimpleFilter('filterBySection', [$this, 'filterBySection']),
63
            new \Twig_SimpleFilter('filterBy', [$this, 'filterBy']),
64
            new \Twig_SimpleFilter('sortByTitle', [$this, 'sortByTitle']),
65
            new \Twig_SimpleFilter('sortByWeight', [$this, 'sortByWeight']),
66
            new \Twig_SimpleFilter('sortByDate', [$this, 'sortByDate']),
67
            new \Twig_SimpleFilter('urlize', [$this, 'slugifyFilter']),
68
            new \Twig_SimpleFilter('minifyCSS', [$this, 'minifyCss']),
69
            new \Twig_SimpleFilter('minifyJS', [$this, 'minifyJs']),
70
            new \Twig_SimpleFilter('SCSStoCSS', [$this, 'scssToCss']),
71
            new \Twig_SimpleFilter('excerpt', [$this, 'excerpt']),
72
            new \Twig_SimpleFilter('excerptHtml', [$this, 'excerptHtml']),
73
        ];
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function getFunctions()
80
    {
81
        return [
82
            new \Twig_SimpleFunction('url', [$this, 'createUrl'], ['needs_environment' => true]),
83
            new \Twig_SimpleFunction('minify', [$this, 'minify']),
84
            new \Twig_SimpleFunction('readtime', [$this, 'readtime']),
85
            new \Twig_SimpleFunction('toCSS', [$this, 'toCss']),
86
            new \Twig_SimpleFunction('hash', [$this, 'hashFile']),
87
            new \Twig_SimpleFunction('getenv', [$this, 'getEnv']),
88
        ];
89
    }
90
91
    /**
92
     * Filter by section.
93
     *
94
     * @param \Cecil\Page\Collection $pages
95
     * @param string                 $section
96
     *
97
     * @return array
98
     */
99
    public function filterBySection($pages, $section)
100
    {
101
        return $this->filterBy($pages, 'section', $section);
102
    }
103
104
    /**
105
     * Filter by variable.
106
     *
107
     * @param \Cecil\Page\Collection $pages
108
     * @param string                 $variable
109
     * @param string                 $value
110
     *
111
     * @throws Exception
112
     *
113
     * @return array
114
     */
115
    public function filterBy($pages, $variable, $value)
116
    {
117
        $filteredPages = $pages->filter(function (Page $page) use ($variable, $value) {
118
            // dedicated getter?
119
            $method = 'get'.ucfirst($variable);
120
            if (method_exists($page, $method) && $page->$method() == $value) {
121
                return true;
122
            }
123
            if ($page->getVariable($variable) == $value) {
124
                return true;
125
            }
126
        });
127
128
        return $filteredPages;
129
    }
130
131
    /**
132
     * Sort by title.
133
     *
134
     * @param $array|CollectionInterface
135
     *
136
     * @return mixed
137
     */
138
    public function sortByTitle($array)
139
    {
140
        if ($array instanceof Collection) {
141
            $array = $array->toArray();
142
        }
143
        if (is_array($array)) {
144
            array_multisort(array_keys($array), SORT_NATURAL | SORT_FLAG_CASE, $array);
145
        }
146
147
        return $array;
148
    }
149
150
    /**
151
     * Sort by weight.
152
     *
153
     * @param $array|CollectionInterface
154
     *
155
     * @return mixed
156
     */
157 View Code Duplication
    public function sortByWeight($array)
158
    {
159
        $callback = function ($a, $b) {
160
            if (!isset($a['weight'])) {
161
                return 1;
162
            }
163
            if (!isset($b['weight'])) {
164
                return -1;
165
            }
166
            if ($a['weight'] == $b['weight']) {
167
                return 0;
168
            }
169
170
            return ($a['weight'] < $b['weight']) ? -1 : 1;
171
        };
172
173
        if ($array instanceof Collection) {
174
            $array = $array->toArray();
175
        }
176
        if (is_array($array)) {
177
            usort($array, $callback);
178
        }
179
180
        return $array;
181
    }
182
183
    /**
184
     * Sort by date.
185
     *
186
     * @param $array|CollectionInterface
187
     *
188
     * @return mixed
189
     */
190 View Code Duplication
    public function sortByDate($array)
191
    {
192
        $callback = function ($a, $b) {
193
            if (!isset($a['date'])) {
194
                return -1;
195
            }
196
            if (!isset($b['date'])) {
197
                return 1;
198
            }
199
            if ($a['date'] == $b['date']) {
200
                return 0;
201
            }
202
203
            return ($a['date'] > $b['date']) ? -1 : 1;
204
        };
205
206
        if ($array instanceof Collection) {
207
            $array = $array->toArray();
208
        }
209
        if (is_array($array)) {
210
            usort($array, $callback);
211
        }
212
213
        return $array;
214
    }
215
216
    /**
217
     * Create an URL.
218
     *
219
     * $options[
220
     *     'canonical' => null,
221
     *     'addhash'   => true,
222
     * ];
223
     *
224
     * @param \Twig_Environment            $env
225
     * @param string|\Cecil\Page\Page|null $value
226
     * @param array|null                   $options
227
     *
228
     * @return string|null
0 ignored issues
show
Documentation introduced by
Should the return type not be string|\Cecil\Page\Page|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
229
     */
230
    public function createUrl(\Twig_Environment $env, $value = null, $options = null)
231
    {
232
        $base = '';
233
        $baseurl = $env->getGlobals()['site']['baseurl'];
234
        $hash = md5($env->getGlobals()['site']['time']);
235
        $canonical = null;
236
        $addhash = true;
237
238
        if (isset($options['canonical'])) {
239
            $canonical = $options['canonical'];
240
        }
241
        if (is_bool($options)) { // backward compatibility
242
            $canonical = $options;
243
        }
244
        if (isset($options['addhash'])) {
245
            $addhash = $options['addhash'];
246
        }
247
248
        if ($env->getGlobals()['site']['canonicalurl'] === true || $canonical === true) {
249
            $base = rtrim($baseurl, '/');
250
        }
251
        if ($canonical === false) {
252
            $base = '';
253
        }
254
255
        if ($value instanceof Page) {
256
            $value = $value->getPermalink();
257
            if (false !== strpos($value, '.')) { // file URL (with a dot for extension)
258
                $url = $base.'/'.ltrim($value, '/');
259
            } else {
260
                $url = $base.'/'.ltrim(rtrim($value, '/').'/', '/');
261
            }
262
        } else {
263
            if (preg_match('~^(?:f|ht)tps?://~i', $value)) { // external URL
264
                $url = $value;
265
            } elseif (false !== strpos($value, '.')) { // file URL (with a dot for extension)
266
                $url = $base.'/'.ltrim($value, '/');
267
                if ($addhash) {
268
                    $url .= '?'.$hash;
269
                }
270
            } else {
271
                $url = $base.'/';
272
                if (!empty($value)) {
273
                    $value = $this->slugifyFilter($value);
274
                    $url .= ltrim(rtrim($value, '/').'/', '/');
275
                }
276
            }
277
        }
278
279
        return $url;
280
    }
281
282
    /**
283
     * Minify a CSS or a JS file.
284
     *
285
     * @param string $path
286
     *
287
     * @throws Exception
288
     *
289
     * @return string
290
     */
291
    public function minify($path)
292
    {
293
        $filePath = $this->destPath.'/'.$path;
294
        if (is_file($filePath)) {
295
            $extension = (new \SplFileInfo($filePath))->getExtension();
296
            switch ($extension) {
297
                case 'css':
298
                    $minifier = new Minify\CSS($filePath);
299
                    break;
300
                case 'js':
301
                    $minifier = new Minify\JS($filePath);
302
                    break;
303
                default:
304
                    throw new Exception(sprintf("File '%s' should be a '.css' or a '.js'!", $path));
305
            }
306
            $minifier->minify($filePath);
307
308
            return $path;
309
        }
310
311
        throw new Exception(sprintf("File '%s' doesn't exist!", $path));
312
    }
313
314
    /**
315
     * Minify CSS.
316
     *
317
     * @param $value
318
     *
319
     * @return string
320
     */
321
    public function minifyCss($value)
322
    {
323
        $minifier = new Minify\CSS($value);
324
325
        return $minifier->minify();
326
    }
327
328
    /**
329
     * Minify JS.
330
     *
331
     * @param $value
332
     *
333
     * @return string
334
     */
335
    public function minifyJs($value)
336
    {
337
        $minifier = new Minify\JS($value);
338
339
        return $minifier->minify();
340
    }
341
342
    /**
343
     * Compile style file to CSS.
344
     *
345
     * @param string $path
346
     *
347
     * @throws Exception
348
     *
349
     * @return string
350
     */
351
    public function toCss($path)
352
    {
353
        $filePath = $this->destPath.'/'.$path;
354
        $subPath = substr($path, 0, strrpos($path, '/'));
355
356
        if (is_file($filePath)) {
357
            $extension = (new \SplFileInfo($filePath))->getExtension();
358
            switch ($extension) {
359
                case 'scss':
360
                    $scssPhp = new Compiler();
361
                    $scssPhp->setImportPaths($this->destPath.'/'.$subPath);
362
                    $targetPath = preg_replace('/scss/m', 'css', $path);
363
364
                    // compile if target file doesn't exists
365
                    if (!$this->fileSystem->exists($this->destPath.'/'.$targetPath)) {
366
                        $scss = file_get_contents($filePath);
367
                        $css = $scssPhp->compile($scss);
368
                        $this->fileSystem->dumpFile($this->destPath.'/'.$targetPath, $css);
369
                    }
370
371
                    return $targetPath;
372
                default:
373
                    throw new Exception(sprintf("File '%s' should be a '.scss'!", $path));
374
            }
375
        }
376
377
        throw new Exception(sprintf("File '%s' doesn't exist!", $path));
378
    }
379
380
    /**
381
     * Compile SCSS string to CSS.
382
     *
383
     * @param $value
384
     *
385
     * @return string
386
     */
387
    public function scssToCss($value)
388
    {
389
        $scss = new Compiler();
390
391
        return $scss->compile($value);
392
    }
393
394
    /**
395
     * Read $lenght first characters of a string and add a suffix.
396
     *
397
     * @param $string
398
     * @param int    $length
399
     * @param string $suffix
400
     *
401
     * @return string
402
     */
403
    public function excerpt($string, $length = 450, $suffix = ' …')
404
    {
405
        $string = str_replace('</p>', '<br /><br />', $string);
406
        $string = trim(strip_tags($string, '<br>'), '<br />');
407
        if (mb_strlen($string) > $length) {
408
            $string = mb_substr($string, 0, $length);
409
            $string .= $suffix;
410
        }
411
412
        return $string;
413
    }
414
415
    /**
416
     * Read characters before '<!-- excerpt -->'.
417
     *
418
     * @param $string
419
     *
420
     * @return string
421
     */
422
    public function excerptHtml($string)
423
    {
424
        // https://regex101.com/r/mA2mG0/3
425
        $pattern = '^(.*)[\n\r\s]*<!-- excerpt -->[\n\r\s]*(.*)$';
426
        preg_match(
427
            '/'.$pattern.'/s',
428
            $string,
429
            $matches
430
        );
431
        if (empty($matches)) {
432
            return $string;
433
        }
434
435
        return trim($matches[1]);
436
    }
437
438
    /**
439
     * Calculate estimated time to read a text.
440
     *
441
     * @param $text
442
     *
443
     * @return float|string
444
     */
445
    public function readtime($text)
446
    {
447
        $words = str_word_count(strip_tags($text));
448
        $min = floor($words / 200);
449
        if ($min === 0) {
450
            return '1';
451
        }
452
453
        return $min;
454
    }
455
456
    /**
457
     * Hash file with sha384.
458
     *
459
     * @param string $path
460
     *
461
     * @return string|null
462
     */
463
    public function hashFile($path)
464
    {
465
        if (is_file($filePath = $this->destPath.'/'.$path)) {
466
            return sprintf('sha384-%s', base64_encode(hash_file('sha384', $filePath, true)));
467
        }
468
    }
469
470
    /**
471
     * Gets the value of an environment variable.
472
     *
473
     * @param string $var
474
     *
475
     * @return string|false
476
     */
477
    public function getEnv($var)
478
    {
479
        return getenv($var);
480
    }
481
}
482