Completed
Push — master ( 300463...799667 )
by Vitaly
23:30 queued 18:38
created

Module::prepare()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.0961

Importance

Changes 10
Bugs 0 Features 5
Metric Value
c 10
b 0
f 5
dl 0
loc 20
ccs 9
cts 11
cp 0.8182
rs 9.2
cc 4
eloc 10
nc 8
nop 1
crap 4.0961
1
<?php
2
namespace samsonphp\less;
3
4
use samson\core\ExternalModule;
5
use samsonframework\filemanager\FileManagerInterface;
6
use samsonframework\localfilemanager\LocalFileManager;
7
use samsonphp\event\Event;
8
use samsonphp\resource\exception\ResourceNotFound;
9
use samsonphp\resource\Router;
10
11
/**
12
 * SamsonPHP LESS compiler module.
13
 * TODO: Change to file operations to FileManagerInterface
14
 *
15
 * @author Vitaly Iegorov <[email protected]>
16
 */
17
class Module extends ExternalModule
18
{
19
    /** LESS mixin declaration pattern */
20
    const P_IMPORT_DECLARATION = '/@import\s+(\'|\")(?<path>[^\'\"]+)(\'|\");/';
21
22
    /** LESS resource importing dependencies file name */
23
    const DEPENDENCY_CACHE = 'dependencies';
24
25
    /** @var array LESS resources dependencies */
26
    public $dependencies = [];
27
28
    /** @var string Path to LESS resources dependencies cache file */
29
    protected $dependencyCache;
30
31
    /** @var \lessc LESS compiler */
32
    protected $less;
33
34
    /** @var FileManagerInterface */
35
    protected $fileManager;
36
37
    /** SamsonFramework load preparation stage handler */
38 6
    public function prepare(array $params = [])
39
    {
40 6
        $moduleCachePath = array_key_exists('cachePath', $params) ? $params['cachePath'] : $this->cache_path;
41 6
        $this->dependencyCache = $moduleCachePath.self::DEPENDENCY_CACHE;
42
43
        // Load file manager
44 6
        $this->fileManager = array_key_exists('fileManager', $params) ? $params['fileManager'] : new LocalFileManager();
45
46
        // Read previous cache file
47 6
        if ($this->fileManager->exists($this->dependencyCache)) {
48
            $this->dependencies = unserialize($this->fileManager->read($this->dependencyCache));
49
        }
50
51 6
        $this->less = new \lessc;
52
53 6
        Event::subscribe(Router::E_RESOURCE_COMPILE, [$this, 'compiler']);
54 6
        Event::subscribe(Router::E_FINISHED, [$this, 'cacheDependencies']);
55
56 6
        return parent::prepare();
57
    }
58
59
    /**
60
     * Cache LESS resources importing dependency trees.
61
     */
62 1
    public function cacheDependencies()
63
    {
64 1
        $this->fileManager->write($this->dependencyCache, serialize($this->dependencies));
65 1
    }
66
67
    /**
68
     * Recursively replace @import in content of the LESS file
69
     *
70
     * @param string $resource Resource full path
71
     * @param string $content  less file content
72
     *
73
     * @return string Content of LESS file with included @imported resources
74
     * @throws ResourceNotFound If importing resource could not be found
75
     */
76
    protected function readImport($resource, $content)
77
    {
78
        // Rewrite imports
79
        $matches = [];
80
        if (preg_match_all(self::P_IMPORT_DECLARATION, $content, $matches)) {
81
            for ($i=0, $size = count($matches[0]); $i < $size; $i++) {
82
                // Build absolute path to imported resource
83
                $path = dirname($resource).DIRECTORY_SEPARATOR.$matches['path'][$i];
84
85
                // Append .less extension according to standard
86
                if (false === ($path = realpath($this->fileManager->exists($path)?$path:$path.'.less'))) {
87
                    throw new ResourceNotFound('Cannot import file: '.$matches['path'][$i]);
88
                }
89
90
                // Add parent to child dependency
91
                $this->dependencies[$path][$resource] = [];
92
93
                // Replace path in LESS @import command with recursive call to this function
94
                $content = str_replace($matches[0][$i], $this->readImport($path, $this->fileManager->read($path)), $content);
95
            }
96
        }
97
98
        return $content;
99
    }
100
101
    /**
102
     * LESS resource compiler.
103
     *
104
     * @param string $resource  Resource full path
105
     * @param string $extension Resource extension
106
     * @param string $content   Compiled output resource content
107
     * @param array $dependencies Collection of compiled resource dependent modules
108
     *
109
     * @throws \Exception
110
     */
111
    public function compiler($resource, &$extension, &$content, &$dependencies)
112
    {
113
        if ($extension === 'less') {
114
            try {
115
                // Rewrite imports
116
                $content = $this->readImport($resource, $content);
117
118
                // Compile LESS content to CSS
119
                $content = $this->less->compile($content);
120
121
                // Switch extension
122
                $extension = 'css';
123
124
                // Return dependencies for this resource
125
                $dependencies = array_key_exists($resource, $this->dependencies)
126
                    ? $this->dependencies[$resource]
127
                    : [];
128
            } catch (\Exception $e) {
129
                //$errorFile = 'cache/error_resourcer'.microtime(true).'.less';
130
                //file_put_contents($errorFile, $output);
131
                throw new \Exception('Failed compiling LESS in "' . $resource . '":' . "\n" . $e->getMessage());
132
            }
133
        }
134
    }
135
}
136