Completed
Push — master ( 2050be...1b2ead )
by Vitaly
34:53 queued 26:07
created

Module   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 30%

Importance

Changes 24
Bugs 3 Features 8
Metric Value
wmc 14
c 24
b 3
f 8
lcom 1
cbo 4
dl 0
loc 118
ccs 12
cts 40
cp 0.3
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A prepare() 0 17 3
A cacheDependencies() 0 9 2
B readImport() 0 24 5
B compiler() 0 24 4
1
<?php
2
namespace samsonphp\less;
3
4
use samson\core\ExternalModule;
5
use samsonphp\event\Event;
6
use samsonphp\resource\exception\ResourceNotFound;
7
use samsonphp\resource\Router;
8
9
/**
10
 * SamsonPHP LESS compiler module.
11
 * TODO: Change to file operations to FileManagerInterface
12
 *
13
 * @author Vitaly Iegorov <[email protected]>
14
 */
15
class Module extends ExternalModule
16
{
17
    /** LESS mixin declaration pattern */
18
    const P_IMPORT_DECLARATION = '/@import\s+(\'|\")(?<path>[^\'\"]+)(\'|\");/';
19
20
    /** LESS resource importing dependencies file name */
21
    const DEPENDENCY_CACHE = 'dependencies';
22
23
    /** @var array LESS resources dependencies */
24
    public $dependencies = [];
25
26
    /** @var string Path to LESS resources dependencies cache file */
27
    protected $dependencyCache;
28
29
    /** @var \lessc LESS compiler */
30
    protected $less;
31
32
    /** SamsonFramework load preparation stage handler */
33 6
    public function prepare(array $params = [])
34
    {
35 6
        $moduleCachePath = array_key_exists('cachePath', $params) ? $params['cachePath'] : $this->cache_path;
36 6
        $this->dependencyCache = $moduleCachePath.self::DEPENDENCY_CACHE;
37
38
        // Read previous cache file
39 6
        if (file_exists($this->dependencyCache)) {
40
            $this->dependencies = unserialize(file_get_contents($this->dependencyCache));
41
        }
42
43 6
        $this->less = new \lessc;
44
45 6
        Event::subscribe(Router::E_RESOURCE_COMPILE, [$this, 'compiler']);
46 6
        Event::subscribe(Router::E_FINISHED, [$this, 'cacheDependencies']);
47
48 6
        return parent::prepare();
49
    }
50
51
    /**
52
     * Cache LESS resources importing dependency trees.
53
     */
54 1
    public function cacheDependencies()
55
    {
56
        // Create folder
57 1
        if (!file_exists(dirname($this->dependencyCache))) {
58
            @mkdir(dirname($this->dependencyCache), 0777, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
59
        }
60
61 1
        file_put_contents($this->dependencyCache, serialize($this->dependencies));
62 1
    }
63
64
    /**
65
     * Recursively replace @import in content of the LESS file
66
     *
67
     * @param string $resource Resource full path
68
     * @param string $content  less file content
69
     *
70
     * @return string Content of LESS file with included @imported resources
71
     * @throws ResourceNotFound If importing resource could not be found
72
     */
73
    protected function readImport($resource, $content)
74
    {
75
        // Rewrite imports
76
        $matches = [];
77
        if (preg_match_all(self::P_IMPORT_DECLARATION, $content, $matches)) {
78
            for ($i=0, $size = count($matches[0]); $i < $size; $i++) {
79
                // Build absolute path to imported resource
80
                $path = dirname($resource).DIRECTORY_SEPARATOR.$matches['path'][$i];
81
82
                // Append .less extension according to standard
83
                if (false === ($path = realpath(is_file($path)?$path:$path.'.less'))) {
84
                    throw new ResourceNotFound('Cannot import file: '.$matches['path'][$i]);
85
                }
86
87
                // Add parent to child dependency
88
                $this->dependencies[$path][$resource] = [];
89
90
                // Replace path in LESS @import command with recursive call to this function
91
                $content = str_replace($matches[0][$i], $this->readImport($path, file_get_contents($path)), $content);
92
            }
93
        }
94
95
        return $content;
96
    }
97
98
    /**
99
     * LESS resource compiler.
100
     *
101
     * @param string $resource  Resource full path
102
     * @param string $extension Resource extension
103
     * @param string $content   Compiled output resource content
104
     * @param array $dependencies Collection of compiled resource dependent modules
105
     *
106
     * @throws \Exception
107
     */
108
    public function compiler($resource, &$extension, &$content, &$dependencies)
109
    {
110
        if ($extension === 'less') {
111
            try {
112
                // Rewrite imports
113
                $content = $this->readImport($resource, $content);
114
115
                // Compile LESS content to CSS
116
                $content = $this->less->compile($content);
117
118
                // Switch extension
119
                $extension = 'css';
120
121
                // Return dependencies for this resource
122
                $dependencies = array_key_exists($resource, $this->dependencies)
123
                    ? $this->dependencies[$resource]
124
                    : [];
125
            } catch (\Exception $e) {
126
                //$errorFile = 'cache/error_resourcer'.microtime(true).'.less';
127
                //file_put_contents($errorFile, $output);
128
                throw new \Exception('Failed compiling LESS in "' . $resource . '":' . "\n" . $e->getMessage());
129
            }
130
        }
131
    }
132
}
133