Completed
Push — master ( 9ed0b2...146511 )
by Adam
17:10
created

ThemeLocator::getThemeFolders()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
/*
3
 * WellCommerce Open-Source E-Commerce Platform
4
 * 
5
 * This file is part of the WellCommerce package.
6
 *
7
 * (c) Adam Piotrowski <[email protected]>
8
 * 
9
 * For the full copyright and license information,
10
 * please view the LICENSE file that was distributed with this source code.
11
 */
12
13
namespace WellCommerce\Bundle\AppBundle\Service\Theme\Locator;
14
15
use Symfony\Component\Finder\Finder;
16
use Symfony\Component\Finder\SplFileInfo;
17
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
18
use Symfony\Component\HttpKernel\KernelInterface;
19
use WellCommerce\Bundle\AppBundle\Service\Theme\Storage\ThemeStorageInterface;
20
21
/**
22
 * Class ThemeLocator
23
 *
24
 * @author  Adam Piotrowski <[email protected]>
25
 */
26
final class ThemeLocator implements ThemeLocatorInterface
27
{
28
    /**
29
     * @var ThemeStorageInterface
30
     */
31
    private $themeStorage;
32
    
33
    /**
34
     * @var KernelInterface
35
     */
36
    private $kernel;
37
    
38
    /**
39
     * @var string
40
     */
41
    private $fallBackTheme;
42
    
43
    /**
44
     * @var string
45
     */
46
    private $themesDir;
47
    
48
    /**
49
     * @var array
50
     */
51
    private $themeFolders = [];
52
    
53
    /**
54
     * ThemeLocator constructor.
55
     *
56
     * @param KernelInterface       $kernel
57
     * @param ThemeStorageInterface $themeStorage
58
     * @param string                $fallbackTheme
59
     * @param string                $themesDir
60
     */
61
    public function __construct(KernelInterface $kernel, ThemeStorageInterface $themeStorage, string $fallbackTheme, string $themesDir)
62
    {
63
        $this->kernel        = $kernel;
64
        $this->fallBackTheme = $fallbackTheme;
65
        $this->themeStorage  = $themeStorage;
66
        $this->themesDir     = $themesDir;
67
    }
68
    
69
    public function getCurrentThemeFolder(): string
70
    {
71
        if (false === $this->themeStorage->hasCurrentTheme()) {
72
            return $this->fallBackTheme;
73
        }
74
        
75
        return $this->themeStorage->getCurrentThemeFolder();
76
    }
77
    
78
    public function getThemesDirectory(): string
79
    {
80
        return $this->themesDir;
81
    }
82
    
83
    public function getThemeFolders(): array
84
    {
85
        if (empty($this->themeFolders)) {
86
            $this->themeFolders = $this->scanThemesDirectory();
87
        }
88
        
89
        return $this->themeFolders;
90
    }
91
    
92
    public function locateTemplate(string $name): string
93
    {
94
        if (!$this->isValidFilename($name)) {
95
            throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name));
96
        }
97
        
98
        list($bundleName, $path) = $this->getBundleNameAndPath($name);
99
        
100
        if (0 !== strpos($path, 'Resources')) {
101
            throw new \RuntimeException('Template files have to be in Resources.');
102
        }
103
        
104
        $bundles = $this->kernel->getBundle($bundleName, false);
105
        
106
        $parameters = [
107
            '%themes_path%'   => $this->getThemesDirectory(),
108
            '%current_theme%' => $this->getCurrentThemeFolder(),
109
            '%template%'      => substr($path, strlen('Resources/views/')),
110
        ];
111
        
112
        return $this->locateBundlesResource($bundles, $parameters, $path);
113
    }
114
    
115
    private function getBundleNameAndPath(string $name): array
116
    {
117
        $bundleName = substr($name, 1);
118
        $path       = '';
119
        
120
        if (false !== strpos($bundleName, '/')) {
121
            list($bundleName, $path) = explode('/', $bundleName, 2);
122
        }
123
        
124
        return [
125
            $bundleName,
126
            $path,
127
        ];
128
    }
129
    
130
    protected function locateBundlesResource(array $bundles, array $parameters, string $name): string
131
    {
132
        $themePaths    = [];
133
        $resourcePaths = [];
134
        
135
        foreach ($bundles as $bundle) {
136
            $this->locateThemePathForBundleResource($bundle, $parameters, $themePaths);
137
            $this->getDefaultBundleResourcePath($bundle, $name, $resourcePaths);
138
        }
139
        
140
        $paths = array_merge($themePaths, $resourcePaths);
141
        
142
        if (count($paths)) {
143
            return current($paths);
144
        }
145
        
146
        throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name));
147
    }
148
    
149
    private function locateThemePathForBundleResource(BundleInterface $bundle, array $parameters, array &$checkPaths)
150
    {
151
        $parameters = array_merge($parameters, [
152
            '%bundle_name%' => $bundle->getName(),
153
        ]);
154
        
155
        $path = strtr(self::THEME_PATH_PATTERN, $parameters);
156
        
157
        if ($this->isValidPath($path)) {
158
            $checkPaths[] = $path;
159
        }
160
    }
161
    
162
    private function getDefaultBundleResourcePath(BundleInterface $bundle, string $path, array &$resourcePaths)
163
    {
164
        $file = $bundle->getPath() . '/' . $path;
165
        if ($this->isValidPath($file)) {
166
            $resourcePaths[] = $file;
167
        }
168
    }
169
    
170
    private function isValidPath(string $path): bool
171
    {
172
        return file_exists($path);
173
    }
174
    
175
    private function isValidFilename(string $name): bool
176
    {
177
        return (false === strpos($name, '..'));
178
    }
179
    
180
    private function scanThemesDirectory(): array
181
    {
182
        $folders     = [];
183
        $finder      = new Finder();
184
        $directories = $finder->directories()->in($this->themesDir)->sortByName()->depth('== 1');
185
        
186
        /** @var SplFileInfo $directory */
187
        foreach ($directories as $directory) {
188
            $name           = $directory->getRelativePath();
189
            $folders[$name] = $name;
190
        }
191
        
192
        return $folders;
193
    }
194
}
195