Completed
Push — master ( f42b1f...ed96b3 )
by Craig
04:43
created

Asset   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 85
Duplicated Lines 0 %

Importance

Changes 4
Bugs 2 Features 0
Metric Value
eloc 35
c 4
b 2
f 0
dl 0
loc 85
rs 10
wmc 12

3 Methods

Rating   Name   Duplication   Size   Complexity  
A resolve() 0 33 6
A __construct() 0 8 1
A mapZikulaAssetPath() 0 16 5
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\ThemeModule\Engine;
15
16
use InvalidArgumentException;
17
use Symfony\Component\Asset\Packages;
18
use Symfony\Component\HttpKernel\Bundle\Bundle;
19
use Symfony\Component\Routing\RouterInterface;
20
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
21
use Zikula\Core\AbstractBundle;
22
23
/**
24
 * Class Asset
25
 *
26
 * This class locates assets accounting for possible overrides in app/Resources/$bundleName or in the
27
 * active theme. It is foremost used by the zasset() Twig template plugin, but can be utilized as a standalone
28
 * service as well. All asset types (js, css, images) will work.
29
 *
30
 * Asset paths must begin with `@` in order to be processed (and possibly overridden) by this class.
31
 * Assets that do not contain `@` are passed through to the standard symfony asset management.
32
 * Assets from the `/web` directory cannot be overridden.
33
 *
34
 * Overrides are in this order:
35
 *  1) app/Resources/$bundleName/public/* @todo
36
 *  2) $theme/Resources/$bundleName/public/*
37
 *  3) $bundleName/Resources/public/*
38
 */
39
class Asset
40
{
41
    /**
42
     * @var ZikulaHttpKernelInterface
43
     */
44
    private $kernel;
45
46
    /**
47
     * @var Packages
48
     */
49
    private $assetPackages;
50
51
    /**
52
     * @var RouterInterface
53
     */
54
    private $router;
55
56
    public function __construct(
57
        ZikulaHttpKernelInterface $kernel,
58
        Packages $assetPackages,
59
        RouterInterface $router
60
    ) {
61
        $this->kernel = $kernel;
62
        $this->assetPackages = $assetPackages;
63
        $this->router = $router;
64
    }
65
66
    /**
67
     * Returns path for asset.
68
     */
69
    public function resolve(string $path): string
70
    {
71
        // return immediately for straight asset paths
72
        // doesn't check if file exists
73
        if ('@' !== $path[0]) {
74
            if (0 === mb_strpos($path, '/')) {
75
                $path = mb_substr($path, 1);
76
            }
77
78
            return $this->assetPackages->getUrl($path);
79
        } else {
80
            [$bundleName, $originalPath] = explode(':', $path);
81
            $path = $this->mapZikulaAssetPath($bundleName, $originalPath);
82
        }
83
84
        // if file exists in /web, then use it first
85
        $httpRootDir = str_replace($this->router->getContext()->getBaseUrl(), '', $this->kernel->getProjectDir());
86
        $webPath = $this->assetPackages->getUrl($path);
87
        if (false !== realpath($httpRootDir . $webPath)) {
88
            return $webPath;
89
        }
90
91
        // try to locate the asset in the bundle directory or global override
92
        $projectDir = $this->kernel->getProjectDir();
93
        $fullPath = $this->kernel->locateResource($bundleName . '/Resources/public/' . $originalPath);
94
        if (false === realpath($fullPath)) {
95
            // try to find the asset in the global override path.  @todo update for Symfony 5 structure
96
            $fullPath = $this->kernel->locateResource('app/Resources/public/' . $originalPath);
97
        }
98
        $resultPath = false !== mb_strpos($fullPath, $projectDir) ? str_replace($projectDir, '', $fullPath) : $fullPath;
99
        $resultPath = str_replace(DIRECTORY_SEPARATOR, '/', $resultPath);
100
101
        return $this->assetPackages->getUrl($resultPath, 'zikula_default');
102
    }
103
104
    /**
105
     * Maps zasset path argument
106
     * e.g. "@AcmeBundle:css/foo.css" to `AcmeBundle/Resources/public/css/foo.css`
107
     */
108
    private function mapZikulaAssetPath(?string $bundleName, ?string $path): string
109
    {
110
        if (!isset($bundleName) || !isset($path)) {
111
            throw new InvalidArgumentException('No bundle name resolved, must be like "@AcmeBundle:css/foo.css"');
112
        }
113
        $bundle = $this->kernel->getBundle(mb_substr($bundleName, 1));
114
        if ($bundle instanceof Bundle) {
115
            $path = '/' . $path;
116
            if ($bundle instanceof AbstractBundle) {
117
                $path = $bundle->getRelativeAssetPath() . $path;
118
            } else {
119
                $path = mb_strtolower('Bundles/' . mb_substr($bundle->getName(), 0, -mb_strlen('Bundle'))) . $path;
120
            }
121
        }
122
123
        return $path;
124
    }
125
}
126