Completed
Push — master ( 84a134...f1a75f )
by Vitaly
02:31
created

Module   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 13
Bugs 1 Features 4
Metric Value
wmc 15
c 13
b 1
f 4
lcom 1
cbo 3
dl 0
loc 131
ccs 11
cts 11
cp 1
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A prepare() 0 18 2
A finished() 0 13 2
C analyzer() 0 31 8
A compiler() 0 21 3
1
<?php
2
namespace samsonphp\less;
3
4
use samson\core\ExternalModule;
5
use samsonphp\event\Event;
6
use samsonphp\resource\Router;
7
8
/**
9
 * SamsonPHP LESS compiler module.
10
 *
11
 * @author Vitaly Iegorov <[email protected]>
12
 * @author Nikita Kotenko <[email protected]>
13
 */
14
class Module extends ExternalModule
15
{
16
    /** LESS variable declaration pattern */
17
    const P_VARIABLE_DECLARATION = '/^\s*\@(?<name>[^\s:]+)\s*\:\s*(?<value>[^;]+);/m';
18
    /** LESS mixin declaration pattern */
19
    const P_MIXIN_DECLARATION = '/^\s*\.(?<name>[^\s(:]+)\s*(?<params>\([^)]+\))\s*(?<code>\{.+\})/m';
20 2
21
    /** @var \lessc LESS compiler */
22 2
    protected $less;
23
24 2
    /** @var array Collection of LESS variables */
25
    protected $variables = [];
26 2
27
    /** @var array Collection of LESS mixins */
28
    protected $mixins = [];
29
30
    /** @var string Path to cached mixins & variables */
31
    protected $cachedLESS;
32
33
    /** @var string Cached LESS code */
34
    protected $lessCode;
35
36
    /** SamsonFramework load preparation stage handler */
37
    public function prepare()
38 2
    {
39
        Event::subscribe(Router::E_RESOURCE_PRELOAD, [$this, 'analyzer']);
40
        Event::subscribe(Router::E_RESOURCE_COMPILE, [$this, 'compiler']);
41 2
        Event::subscribe(Router::E_FINISHED, [$this, 'finished']);
42
43
        $this->less = new \lessc;
44 2
45 2
        // Create path to LESS
46
        $this->cachedLESS = $this->cache_path.'mixins.less';
47
48 1
        // Read cached less mixins and variables
49
        if (file_exists($this->cachedLESS)) {
50 1
            $this->lessCode = file_get_contents($this->cachedLESS);
51 1
        }
52
53
        return true;
54
    }
55
56
    /**
57
     * Create LESS variables and mixins cache file.
58
     */
59
    public function finished()
60
    {
61
        $this->lessCode .= implode("\n", $this->variables) . "\n"
62
            . implode("\n", $this->mixins) . "\n";
63
64
        // Create cache path
65
        $path = dirname($this->cachedLESS);
66
        if (!file_exists($path)) {
67
            mkdir($path, 0777, true);
68
        }
69
70
        file_put_contents($this->cachedLESS, $this->lessCode);
71
    }
72
73
    /**
74
     * LESS resource analyzer.
75
     *
76
     * @param string $resource  Resource full path
77
     * @param string $extension Resource extension
78
     * @param string $content LESS code
79
     *
80
     * @return array Variables and mixins collection
81
     */
82
    public function analyzer($resource, $extension, &$content)
83
    {
84
        if ($extension === 'less') {
85
            // Find variable declaration
86
            if (preg_match_all(self::P_VARIABLE_DECLARATION, $content, $matches)) {
87
                // Gather variables in collection key => value
88
                for ($i = 0, $max = count($matches['name']); $i < $max; $i++) {
89
                    if (!array_key_exists($matches['name'][$i], $this->variables)) {
90
                        $this->variables[$matches['name'][$i]] = $matches[0][$i];
91
                        $content = str_replace($matches[0][$i], '', $content);
92
                    }
93
                }
94
            }
95
96
            // TODO: Hack that files with mixin should be separated and have "mixin" in their name
97
            if (strpos($resource, 'mixin') !== false) {
98
                $this->mixins[$resource] = $content;
99
                $content = '';
100
            } elseif (preg_match_all(self::P_MIXIN_DECLARATION, $content, $matches)) {
101
                // Gather variables in collection key => value
102
                for ($i = 0, $max = count($matches[0]); $i < $max; $i++) {
103
                    $this->mixins[$matches['name'][$i]] = $matches[0][$i];
104
                    $content = str_replace($matches[0][$i], '', $content);
105
                }
106
            }
107
108
            return [$this->variables, $this->mixins];
109
        }
110
111
        return [];
112
    }
113
114
    /**
115
     * LESS resource compiler.
116
     *
117
     * @param string $resource  Resource full path
118
     * @param string $extension Resource extension
119
     * @param string $content   Compiled output resource content
120
     *
121
     * @throws \Exception
122
     */
123
    public function compiler($resource, &$extension, &$content)
124
    {
125
        if ($extension === 'less') {
126
            try {
127
                // Read updated CSS resource file and compile it with mixins
128
                $content = $this->less->compile(
129
                    implode("\n", $this->variables) . "\n"
130
                    . implode("\n", $this->mixins) . "\n"
131
                    . $this->lessCode
132
                    . $content
133
                );
134
135
                // Switch extension
136
                $extension = 'css';
137
            } catch (\Exception $e) {
138
                //$errorFile = 'cache/error_resourcer'.microtime(true).'.less';
139
                //file_put_contents($errorFile, $output);
140
                throw new \Exception('Failed compiling LESS in "' . $resource . '":' . "\n" . $e->getMessage());
141
            }
142
        }
143
    }
144
}
145