1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Superdesk Web Publisher Core Bundle. |
5
|
|
|
* |
6
|
|
|
* Copyright 2016 Sourcefabric z.u. and contributors. |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please see the |
9
|
|
|
* AUTHORS and LICENSE files distributed with this source code. |
10
|
|
|
* |
11
|
|
|
* @copyright 2016 Sourcefabric z.ú |
12
|
|
|
* @license http://www.superdesk.org/license |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace SWP\Bundle\CoreBundle\Locator; |
16
|
|
|
|
17
|
|
|
use SWP\Bundle\CoreBundle\Detection\DeviceDetectionInterface; |
18
|
|
|
use Sylius\Bundle\ThemeBundle\Model\ThemeInterface; |
19
|
|
|
use Sylius\Bundle\ThemeBundle\Twig\Locator\TemplateNotFoundException; |
20
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
21
|
|
|
use Symfony\Component\HttpKernel\Bundle\BundleInterface; |
22
|
|
|
use Symfony\Component\HttpKernel\KernelInterface; |
23
|
|
|
|
24
|
|
|
class BundleResourceLocator |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @var Filesystem |
28
|
|
|
*/ |
29
|
|
|
private $filesystem; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var KernelInterface |
33
|
|
|
*/ |
34
|
|
|
private $kernel; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var DeviceDetectionInterface |
38
|
|
|
*/ |
39
|
|
|
private $deviceDetection; |
40
|
|
|
|
41
|
|
|
public function __construct(Filesystem $filesystem, KernelInterface $kernel, DeviceDetectionInterface $deviceDetection) |
42
|
|
|
{ |
43
|
|
|
$this->filesystem = $filesystem; |
44
|
|
|
$this->kernel = $kernel; |
45
|
|
|
$this->deviceDetection = $deviceDetection; |
46
|
101 |
|
} |
47
|
|
|
|
48
|
101 |
|
/** |
49
|
101 |
|
* {@inheritdoc} |
50
|
101 |
|
*/ |
51
|
101 |
|
public function locate(string $resourcePath, ThemeInterface $theme): string |
52
|
|
|
{ |
53
|
|
|
$this->assertResourcePathIsValid($resourcePath); |
54
|
|
|
|
55
|
|
|
if (false !== strpos($resourcePath, 'Bundle/Resources/views/')) { |
56
|
|
|
// When using bundle notation, we get a path like @AcmeBundle/Resources/views/template.html.twig |
57
|
|
|
return $this->locateResourceBasedOnBundleNotation($resourcePath, $theme); |
58
|
19 |
|
} |
59
|
|
|
|
60
|
19 |
|
// When using namespaced Twig paths, we get a path like @Acme/template.html.twig |
61
|
19 |
|
return $this->locateResourceBasedOnTwigNamespace($resourcePath, $theme); |
62
|
19 |
|
} |
63
|
19 |
|
|
64
|
|
|
private function assertResourcePathIsValid(string $resourcePath): void |
65
|
|
|
{ |
66
|
|
|
if (0 !== strpos($resourcePath, '@')) { |
67
|
19 |
|
throw new \InvalidArgumentException(sprintf('Bundle resource path (given "%s") should start with an "@".', $resourcePath)); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
if (false !== strpos($resourcePath, '..')) { |
71
|
|
|
throw new \InvalidArgumentException(sprintf('File name "%s" contains invalid characters (..).', $resourcePath)); |
72
|
|
|
} |
73
|
|
|
} |
74
|
19 |
|
|
75
|
|
|
private function locateResourceBasedOnBundleNotation(string $resourcePath, ThemeInterface $theme): string |
76
|
19 |
|
{ |
77
|
19 |
|
$bundleName = substr($resourcePath, 1, strpos($resourcePath, '/') - 1); |
78
|
19 |
|
$resourceName = substr($resourcePath, strpos($resourcePath, 'Resources/') + strlen('Resources/')); |
79
|
19 |
|
|
80
|
19 |
|
// Symfony 4.0+ always returns a single bundle |
81
|
19 |
|
/** @var BundleInterface|BundleInterface[] $bundles */ |
82
|
19 |
|
$bundles = $this->kernel->getBundle($bundleName, false); |
|
|
|
|
83
|
14 |
|
|
84
|
|
|
// So we need to hack it to support both Symfony 3.4 and Symfony 4.0+ |
85
|
19 |
|
if (!is_array($bundles)) { |
86
|
|
|
$bundles = [$bundles]; |
87
|
|
|
} |
88
|
|
|
|
89
|
19 |
|
foreach ($bundles as $bundle) { |
90
|
|
View Code Duplication |
if (null !== $this->deviceDetection->getType()) { |
|
|
|
|
91
|
|
|
$path = sprintf('%s/%s/%s/%s', $theme->getPath(), $this->deviceDetection->getType(), $bundle->getName(), $resourceName); |
92
|
|
|
if ($this->filesystem->exists($path)) { |
93
|
|
|
return $path; |
94
|
|
|
} |
95
|
19 |
|
} |
96
|
|
|
|
97
|
19 |
|
$path = sprintf('%s/%s/%s', $theme->getPath(), $bundle->getName(), $resourceName); |
98
|
|
|
if ($this->filesystem->exists($path)) { |
99
|
|
|
return $path; |
100
|
|
|
} |
101
|
19 |
|
} |
102
|
|
|
|
103
|
|
|
throw new TemplateNotFoundException($resourceName, [$theme]); |
104
|
|
|
} |
105
|
19 |
|
|
106
|
|
|
private function locateResourceBasedOnTwigNamespace(string $resourcePath, ThemeInterface $theme): string |
107
|
|
|
{ |
108
|
19 |
|
$twigNamespace = substr($resourcePath, 1, strpos($resourcePath, '/') - 1); |
109
|
|
|
$resourceName = substr($resourcePath, strpos($resourcePath, '/') + 1); |
110
|
|
View Code Duplication |
if (null !== $this->deviceDetection->getType()) { |
|
|
|
|
111
|
|
|
$path = sprintf('%s/%s/%s/%s', $theme->getPath(), $this->deviceDetection->getType(), $this->getBundleOrPluginName($twigNamespace), $resourceName); |
112
|
|
|
if ($this->filesystem->exists($path)) { |
113
|
|
|
return $path; |
114
|
|
|
} |
115
|
19 |
|
} |
116
|
|
|
|
117
|
19 |
|
$path = sprintf('%s/%s/views/%s', $theme->getPath(), $this->getBundleOrPluginName($twigNamespace), $resourceName); |
118
|
|
|
|
119
|
|
|
if ($this->filesystem->exists($path)) { |
120
|
|
|
return $path; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
throw new TemplateNotFoundException($resourceName, [$theme]); |
124
|
|
|
} |
125
|
19 |
|
|
126
|
|
|
private function getBundleOrPluginName(string $twigNamespace): string |
127
|
19 |
|
{ |
128
|
|
|
if ('Plugin' === substr($twigNamespace, -6)) { |
129
|
|
|
return $twigNamespace; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
return $twigNamespace.'Bundle'; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
public function supports(string $template): bool |
136
|
|
|
{ |
137
|
|
|
return strpos($template, '@') !== 0; |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.