Completed
Push — master ( 4207f0...333332 )
by Kamil
23:19 queued 01:42
created

AssetsInstaller::installThemedBundleAssets()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 0
loc 26
rs 8.8571
c 1
b 1
f 0
cc 3
eloc 14
nc 3
nop 4
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Bundle\ThemeBundle\Asset\Installer;
13
14
use Sylius\Bundle\ThemeBundle\Asset\PathResolverInterface;
15
use Sylius\Bundle\ThemeBundle\Model\ThemeInterface;
16
use Sylius\Bundle\ThemeBundle\Repository\ThemeRepositoryInterface;
17
use Sylius\Bundle\ThemeBundle\HierarchyProvider\ThemeHierarchyProviderInterface;
18
use Symfony\Component\Filesystem\Exception\IOException;
19
use Symfony\Component\Filesystem\Filesystem;
20
use Symfony\Component\Finder\Finder;
21
use Symfony\Component\Finder\SplFileInfo;
22
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
23
use Symfony\Component\HttpKernel\KernelInterface;
24
25
/**
26
 * @author Kamil Kokot <[email protected]>
27
 */
28
class AssetsInstaller implements AssetsInstallerInterface
29
{
30
    /**
31
     * @var Filesystem
32
     */
33
    protected $filesystem;
34
    
35
    /**
36
     * @var KernelInterface
37
     */
38
    protected $kernel;
39
    
40
    /**
41
     * @var ThemeRepositoryInterface
42
     */
43
    protected $themeRepository;
44
45
    /**
46
     * @var ThemeHierarchyProviderInterface
47
     */
48
    protected $themeHierarchyProvider;
49
50
    /**
51
     * @var PathResolverInterface
52
     */
53
    protected $pathResolver;
54
55
    /**
56
     * @param Filesystem $filesystem
57
     * @param KernelInterface $kernel
58
     * @param ThemeRepositoryInterface $themeRepository
59
     * @param ThemeHierarchyProviderInterface $themeHierarchyProvider
60
     * @param PathResolverInterface $pathResolver
61
     */
62
    public function __construct(
63
        Filesystem $filesystem,
64
        KernelInterface $kernel,
65
        ThemeRepositoryInterface $themeRepository,
66
        ThemeHierarchyProviderInterface $themeHierarchyProvider,
67
        PathResolverInterface $pathResolver
68
    ) {
69
        $this->filesystem = $filesystem;
70
        $this->kernel = $kernel;
71
        $this->themeRepository = $themeRepository;
72
        $this->themeHierarchyProvider = $themeHierarchyProvider;
73
        $this->pathResolver = $pathResolver;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function installAssets($targetDir, $symlinkMask)
80
    {
81
        // Create the bundles directory otherwise symlink will fail.
82
        $targetDir = rtrim($targetDir, '/') . '/bundles/';
83
        $this->filesystem->mkdir($targetDir);
84
85
        $effectiveSymlinkMask = $symlinkMask;
86
        foreach ($this->kernel->getBundles() as $bundle) {
87
            $effectiveSymlinkMask = min($effectiveSymlinkMask, $this->installBundleAssets($bundle, $targetDir, $symlinkMask));
88
        }
89
90
        return $effectiveSymlinkMask;
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function installBundleAssets(BundleInterface $bundle, $targetDir, $symlinkMask)
97
    {
98
        $targetDir .= preg_replace('/bundle$/', '', strtolower($bundle->getName()));
99
100
        $this->filesystem->remove($targetDir);
101
102
        $effectiveSymlinkMask = $symlinkMask;
103
        foreach ($this->findAssetsPaths($bundle) as $originDir) {
104
            $effectiveSymlinkMask = min(
105
                $effectiveSymlinkMask,
106
                $this->installVanillaBundleAssets($originDir, $targetDir, $symlinkMask)
107
            );
108
        }
109
110
        foreach ($this->themeRepository->findAll() as $theme) {
111
            $themes = $this->themeHierarchyProvider->getThemeHierarchy($theme);
112
113
            foreach ($this->findAssetsPaths($bundle, $themes) as $originDir) {
114
                $effectiveSymlinkMask = min(
115
                    $effectiveSymlinkMask,
116
                    $this->installThemedBundleAssets($theme, $originDir, $targetDir, $symlinkMask)
117
                );
118
            }
119
        }
120
121
        return $effectiveSymlinkMask;
122
    }
123
124
    /**
125
     * @param ThemeInterface $theme
126
     * @param string $originDir
127
     * @param string $targetDir
128
     * @param int $symlinkMask
129
     *
130
     * @return int
131
     */
132
    protected function installThemedBundleAssets(ThemeInterface $theme, $originDir, $targetDir, $symlinkMask)
133
    {
134
        $effectiveSymlinkMask = $symlinkMask;
135
136
        $finder = new Finder();
137
        $finder->sortByName()->ignoreDotFiles(false)->in($originDir);
138
139
        /** @var SplFileInfo[] $finder */
140
        foreach ($finder as $originFile) {
141
            $targetFile = $targetDir . '/' . $originFile->getRelativePathname();
142
            $targetFile = $this->pathResolver->resolve($targetFile, $theme);
143
144
            if (file_exists($targetFile)) {
145
                continue;
146
            }
147
148
            $this->filesystem->mkdir(dirname($targetFile));
149
150
            $effectiveSymlinkMask = min(
151
                $effectiveSymlinkMask,
152
                $this->installAsset($originFile->getPathname(), $targetFile, $symlinkMask)
153
            );
154
        }
155
156
        return $effectiveSymlinkMask;
157
    }
158
159
    /**
160
     * @param string $originDir
161
     * @param string $targetDir
162
     * @param int $symlinkMask
163
     *
164
     * @return int
165
     */
166
    protected function installVanillaBundleAssets($originDir, $targetDir, $symlinkMask)
167
    {
168
        return $this->installAsset($originDir, $targetDir, $symlinkMask);
169
    }
170
171
    /**
172
     * @param string $origin
173
     * @param string $target
174
     * @param int $symlinkMask
175
     *
176
     * @return int
177
     */
178
    protected function installAsset($origin, $target, $symlinkMask)
179
    {
180
        if (AssetsInstallerInterface::RELATIVE_SYMLINK === $symlinkMask) {
181
            try {
182
                $targetDirname = realpath(is_dir($target) ? $target : dirname($target));
183
                $relativeOrigin = rtrim($this->filesystem->makePathRelative($origin, $targetDirname), '/');
184
185
                $this->doInstallAsset($relativeOrigin, $target, true);
186
187
                return AssetsInstallerInterface::RELATIVE_SYMLINK;
188
            } catch (IOException $exception) {
189
                // Do nothing, trying to create non-relative symlinks later.
190
            }
191
        }
192
193
        if (AssetsInstallerInterface::HARD_COPY !== $symlinkMask) {
194
            try {
195
                $this->doInstallAsset($origin, $target, true);
196
197
                return AssetsInstallerInterface::SYMLINK;
198
            } catch (IOException $exception) {
199
                // Do nothing, hard copy later.
200
            }
201
        }
202
203
        $this->doInstallAsset($origin, $target, false);
204
205
        return AssetsInstallerInterface::HARD_COPY;
206
    }
207
208
    /**
209
     * @param string $origin
210
     * @param string $target
211
     * @param bool $symlink
212
     *
213
     * @throws IOException When failed to make symbolic link, if requested.
214
     */
215
    protected function doInstallAsset($origin, $target, $symlink)
216
    {
217
        if ($symlink) {
218
            $this->doSymlinkAsset($origin, $target);
219
            return;
220
        }
221
222
        $this->doCopyAsset($origin, $target);
223
    }
224
225
    /**
226
     * @param BundleInterface $bundle
227
     * @param ThemeInterface[] $themes
228
     *
229
     * @return array
230
     */
231
    protected function findAssetsPaths(BundleInterface $bundle, array $themes = [])
232
    {
233
        $sources = [];
234
235
        foreach ($themes as $theme) {
236
            $sourceDir = $theme->getPath() . '/' . $bundle->getName() . '/public';
237
            if (is_dir($sourceDir)) {
238
                $sources[] = $sourceDir;
239
            }
240
        }
241
242
        $sourceDir = $bundle->getPath() . '/Resources/public';
243
        if (is_dir($sourceDir)) {
244
            $sources[] = $sourceDir;
245
        }
246
247
        return $sources;
248
    }
249
250
    /**
251
     * @param string $origin
252
     * @param string $target
253
     *
254
     * @throws IOException If symbolic link is broken
255
     */
256
    private function doSymlinkAsset($origin, $target)
257
    {
258
        $this->filesystem->symlink($origin, $target);
259
260
        if (!file_exists($target)) {
261
            throw new IOException('Symbolic link is broken');
262
        }
263
    }
264
265
    /**
266
     * @param string $origin
267
     * @param string $target
268
     */
269
    private function doCopyAsset($origin, $target)
270
    {
271
        if (is_dir($origin)) {
272
            $this->filesystem->mkdir($target, 0777);
273
            $this->filesystem->mirror($origin, $target, Finder::create()->ignoreDotFiles(false)->in($origin));
274
275
            return;
276
        }
277
278
        $this->filesystem->copy($origin, $target);
279
    }
280
}
281