FileResolver.php 5.44 KB
Newer Older
1
<?php
Tino Goratsch's avatar
Tino Goratsch committed
2

3
/**
4
 * Copyright (c) by the ACP3 Developers.
Tino Goratsch's avatar
Tino Goratsch committed
5
 * See the LICENSE file at the top-level module directory for licensing details.
6 7
 */

8 9 10 11
namespace ACP3\Core\Assets;

use ACP3\Core;

12
class FileResolver
13 14 15 16 17
{
    /**
     * @var \ACP3\Core\XML
     */
    protected $xml;
18 19 20 21
    /**
     * @var \ACP3\Core\Environment\ApplicationPath
     */
    protected $appPath;
22 23 24 25
    /**
     * @var \ACP3\Core\Assets\Cache
     */
    protected $resourcesCache;
26
    /**
27
     * @var \ACP3\Core\Modules\Vendor
28 29
     */
    protected $vendors;
30 31 32 33 34 35 36 37 38 39 40
    /**
     * @var array
     */
    protected $cachedPaths = [];
    /**
     * @var bool
     */
    protected $newAssetPathsAdded = false;
    /**
     * @var string
     */
41
    protected $designAssetsPath;
42 43

    /**
44 45 46
     * @param \ACP3\Core\XML                         $xml
     * @param \ACP3\Core\Assets\Cache                $resourcesCache
     * @param \ACP3\Core\Environment\ApplicationPath $appPath
47
     * @param \ACP3\Core\Modules\Vendor              $vendors
48 49 50
     */
    public function __construct(
        Core\XML $xml,
51
        Core\Assets\Cache $resourcesCache,
52
        Core\Environment\ApplicationPath $appPath,
53
        Core\Modules\Vendor $vendors
Tino Goratsch's avatar
Tino Goratsch committed
54
    ) {
55 56
        $this->xml = $xml;
        $this->resourcesCache = $resourcesCache;
57
        $this->appPath = $appPath;
58
        $this->vendors = $vendors;
59 60 61 62
        $this->cachedPaths = $resourcesCache->getCache();
    }

    /**
63
     * Write newly added assets paths into the cache.
64 65 66 67
     */
    public function __destruct()
    {
        if ($this->newAssetPathsAdded === true) {
68
            $this->resourcesCache->saveCache($this->cachedPaths);
69 70 71 72
        }
    }

    /**
Tino Goratsch's avatar
Tino Goratsch committed
73 74 75 76
     * @param string $modulePath
     * @param string $designPath
     * @param string $dir
     * @param string $file
77 78 79 80 81
     *
     * @return string
     */
    public function getStaticAssetPath($modulePath, $designPath, $dir = '', $file = '')
    {
82
        if ($this->needsTrailingSlash($modulePath)) {
83 84
            $modulePath .= '/';
        }
85
        if ($this->needsTrailingSlash($designPath)) {
86 87
            $designPath .= '/';
        }
Tino Goratsch's avatar
Tino Goratsch committed
88
        if (!empty($dir) && !\preg_match('=/$=', $dir)) {
89 90 91
            $dir .= '/';
        }

92
        $systemAssetPath = $this->appPath->getModulesDir() . $modulePath . $dir . $file;
93

Tino Goratsch's avatar
Tino Goratsch committed
94 95 96 97
        // Return early, if the path has been already cached
        if (isset($this->cachedPaths[$systemAssetPath])) {
            return $this->cachedPaths[$systemAssetPath];
        }
98

Tino Goratsch's avatar
Tino Goratsch committed
99
        return $this->resolveAssetPath($modulePath, $designPath, $dir, $file);
100 101
    }

102 103
    /**
     * @param string $path
104
     *
105 106
     * @return bool
     */
107
    protected function needsTrailingSlash($path)
108
    {
Tino Goratsch's avatar
Tino Goratsch committed
109
        return $path !== '' && \strpos($path, '.') === false && !\preg_match('=/$=', $path);
110 111
    }

112
    /**
Tino Goratsch's avatar
Tino Goratsch committed
113 114
     * @param string $modulePath
     * @param string $designPath
115 116
     * @param string $dir
     * @param string $file
117 118 119
     *
     * @return string
     */
Tino Goratsch's avatar
Tino Goratsch committed
120
    private function resolveAssetPath($modulePath, $designPath, $dir, $file)
121
    {
122 123 124 125
        if ($this->designAssetsPath === null) {
            $this->designAssetsPath = $this->appPath->getDesignPathInternal();
        }

126 127 128 129
        $assetPath = '';
        $designAssetPath = $this->designAssetsPath . $designPath . $dir . $file;

        // A theme has overridden a static asset of a module
Tino Goratsch's avatar
Tino Goratsch committed
130
        if (\is_file($designAssetPath) === true) {
131 132 133 134 135 136
            $assetPath = $designAssetPath;
        } else {
            $designInfo = $this->xml->parseXmlFile($this->designAssetsPath . '/info.xml', '/design');

            // Recursively iterate over the nested themes
            if (!empty($designInfo['parent'])) {
137
                $this->designAssetsPath = $this->appPath->getDesignRootPathInternal() . $designInfo['parent'] . '/';
138
                $assetPath = $this->getStaticAssetPath($modulePath, $designPath, $dir, $file);
139
                $this->designAssetsPath = $this->appPath->getDesignPathInternal();
Tino Goratsch's avatar
Tino Goratsch committed
140

141
                return $assetPath;
142 143 144
            }

            // No overrides have been found -> iterate over all possible module namespaces
Tino Goratsch's avatar
Tino Goratsch committed
145
            foreach (\array_reverse($this->vendors->getVendors()) as $vendor) {
146
                $moduleAssetPath = $this->appPath->getModulesDir() . $vendor . '/' . $modulePath . $dir . $file;
Tino Goratsch's avatar
Tino Goratsch committed
147
                if (\is_file($moduleAssetPath) === true) {
148
                    $assetPath = $moduleAssetPath;
Tino Goratsch's avatar
Tino Goratsch committed
149

150 151 152 153 154
                    break;
                }
            }
        }

155
        $systemAssetPath = $this->appPath->getModulesDir() . $modulePath . $dir . $file;
156 157 158 159 160 161 162
        $this->cachedPaths[$systemAssetPath] = $assetPath;
        $this->newAssetPathsAdded = true;

        return $assetPath;
    }

    /**
Tino Goratsch's avatar
Tino Goratsch committed
163
     * @param string $template
164 165 166 167 168 169
     *
     * @return string
     */
    public function resolveTemplatePath($template)
    {
        // A path without any slash was given -> has to be the layout file of the current design
Tino Goratsch's avatar
Tino Goratsch committed
170
        if (\strpos($template, '/') === false) {
171
            return $this->getStaticAssetPath('', '', '', $template);
Tino Goratsch's avatar
Tino Goratsch committed
172 173 174
        }
        // Split the template path in its components
        $fragments = \explode('/', \ucfirst($template));
175

Tino Goratsch's avatar
Tino Goratsch committed
176 177
        if (isset($fragments[2])) {
            $fragments[1] = \ucfirst($fragments[1]);
178
        }
Tino Goratsch's avatar
Tino Goratsch committed
179 180 181 182 183 184 185 186
        $modulesPath = $fragments[0] . '/Resources/';
        $designPath = $fragments[0];
        $template = $fragments[1];
        if (isset($fragments[2])) {
            $template .= '/' . $fragments[2];
        }

        return $this->getStaticAssetPath($modulesPath, $designPath, 'View', $template);
187 188
    }
}