1<?php
2
3/*
4 * This file is part of Component Installer.
5 *
6 * (c) Rob Loach (http://robloach.net)
7 *
8 * For the full copyright and license information, please view the LICENSE.md
9 * file that was distributed with this source code.
10 */
11
12namespace ComponentInstaller\Process;
13
14use Composer\Config;
15use Assetic\Asset\AssetCollection;
16use Assetic\Filter\CssRewriteFilter;
17use Assetic\Asset\FileAsset;
18use Assetic\Filter\FilterCollection;
19
20/**
21 * Builds the require.css file from all Component stylesheets.
22 */
23class RequireCssProcess extends Process
24{
25    /**
26     * {@inheritdoc}
27     */
28    public function process()
29    {
30        $filters = array(new CssRewriteFilter());
31        if ($this->config->has('component-styleFilters')) {
32            $customFilters = $this->config->get('component-styleFilters');
33            if (isset($customFilters) && is_array($customFilters)) {
34                foreach ($customFilters as $filter => $filterParams) {
35                    $reflection = new \ReflectionClass($filter);
36                    $filters[] = $reflection->newInstanceArgs($filterParams);
37                }
38            }
39        }
40
41        $filterCollection = new FilterCollection($filters);
42
43        $assets = new AssetCollection();
44        $styles = $this->packageStyles($this->packages);
45        foreach ($styles as $package => $packageStyles) {
46            $packageAssets = new AssetCollection();
47            $packagePath = $this->componentDir.'/'.$package;
48
49            foreach ($packageStyles as $style => $paths) {
50                foreach ($paths as $path) {
51                    // The full path to the CSS file.
52                    $assetPath = realpath($path);
53                    // The root of the CSS file.
54                    $sourceRoot = dirname($path);
55                    // The style path to the CSS file when external.
56                    $sourcePath = $package . '/' . $style;
57                    //Replace glob patterns with filenames.
58                    $filename = basename($style);
59                    if(preg_match('~^\*(\.[^\.]+)$~', $filename, $matches)){
60                        $sourcePath = str_replace($filename, basename($assetPath), $sourcePath);
61                    }
62                    // Where the final CSS will be generated.
63                    $targetPath = $this->componentDir;
64                    // Build the asset and add it to the collection.
65                    $asset = new FileAsset($assetPath, $filterCollection, $sourceRoot, $sourcePath);
66                    $asset->setTargetPath($targetPath);
67                    $assets->add($asset);
68                    // Add asset to package collection.
69                    $sourcePath = preg_replace('{^.*'.preg_quote($package).'/}', '', $sourcePath);
70                    $asset = new FileAsset($assetPath, $filterCollection, $sourceRoot, $sourcePath);
71                    $asset->setTargetPath($packagePath);
72                    $packageAssets->add($asset);
73                }
74            }
75
76            if (file_put_contents($packagePath.'/'.$package.'-built.css', $packageAssets->dump()) === FALSE) {
77                $this->io->write("<error>Error writing $package-built.css to destination</error>");
78            }
79        }
80
81        if (file_put_contents($this->componentDir . '/require.css', $assets->dump()) === FALSE) {
82            $this->io->write('<error>Error writing require.css to destination</error>');
83            return false;
84        }
85
86        return null;
87    }
88
89    /**
90     * Retrieves an array of styles from a collection of packages.
91     *
92     * @param array $packages
93     *   An array of packages from the composer.lock file.
94     *
95     * @return array
96     *   A set of package styles.
97     */
98    public function packageStyles(array $packages)
99    {
100        $output = array();
101
102        // Construct the packages configuration.
103        foreach ($packages as $package) {
104            // Retrieve information from the extra options.
105            $extra = isset($package['extra']) ? $package['extra'] : array();
106            $name = $this->getComponentName($package['name'], $extra);
107            $component = isset($extra['component']) ? $extra['component'] : array();
108            $styles = isset($component['styles']) ? $component['styles'] : array();
109            $vendorDir = $this->getVendorDir($package);
110
111            // Loop through each style.
112            foreach ($styles as $style) {
113                // Find the style path from the vendor directory.
114                $path = strtr($vendorDir.'/'.$style, '/', DIRECTORY_SEPARATOR);
115
116                // Search for the candidate with a glob recursive file search.
117                $files = $this->fs->recursiveGlobFiles($path);
118                foreach ($files as $file) {
119                    // Provide the package name, style and full path.
120                    $output[$name][$style][] = $file;
121                }
122            }
123        }
124
125        return $output;
126    }
127}
128