Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c3720c1

Browse filesBrowse files
committed
Create ManifestPackages to replace ManifestVersionStrategy
1 parent 0000dfe commit c3720c1
Copy full SHA for c3720c1

14 files changed

+453
-0
lines changed
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Exception;
13+
14+
/**
15+
* Base RuntimeException for the Asset component.
16+
*
17+
* @author Fabien Potencier <fabien@symfony.com>
18+
*/
19+
class RuntimeException extends \RuntimeException implements ExceptionInterface
20+
{
21+
}
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Manifest;
13+
14+
use Symfony\Contracts\Cache\CacheInterface;
15+
16+
class CachedManifest implements ManifestInterface
17+
{
18+
private $manifest;
19+
private $cache;
20+
private $key;
21+
22+
public function __construct(ManifestInterface $manifest, CacheInterface $cache, string $key)
23+
{
24+
$this->manifest = $manifest;
25+
$this->cache = $cache;
26+
}
27+
28+
public function getManifest(): array
29+
{
30+
return $this->cache->get($this->key, [$this->manifest, 'getManifest']);
31+
}
32+
}
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Manifest;
13+
14+
use Symfony\Component\Asset\Exception\RuntimeException;
15+
16+
class JsonManifest implements ManifestInterface
17+
{
18+
private $manifestPath;
19+
20+
public function __construct(string $manifestPath)
21+
{
22+
$this->manifestPath = $manifestPath;
23+
}
24+
25+
public function getManifest(): array
26+
{
27+
if (!is_file($this->manifestPath)) {
28+
throw new RuntimeException(sprintf('Asset manifest file "%s" does not exist.', $this->manifestPath));
29+
}
30+
31+
try {
32+
$data = json_decode(file_get_contents($this->manifestPath), true, 2, \JSON_BIGINT_AS_STRING | (\PHP_VERSION_ID >= 70300 ? \JSON_THROW_ON_ERROR : 0));
33+
} catch (\JsonException $e) {
34+
throw new RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s": ', $this->manifestPath).$e->getMessage(), $e->getCode(), $e);
35+
}
36+
37+
if (0 < json_last_error()) {
38+
throw new RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s": ', $this->manifestPath).json_last_error_msg());
39+
}
40+
41+
return $data;
42+
}
43+
}
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Manifest;
13+
14+
15+
use Symfony\Component\Asset\Exception\RuntimeException;
16+
17+
interface ManifestInterface
18+
{
19+
/**
20+
* @return string[] Map of virtual file names with actual file names.
21+
* @throws RuntimeException When the manifest cannot be loaded.
22+
*/
23+
public function getManifest(): array;
24+
}
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Manifest;
13+
14+
use Symfony\Contracts\HttpClient\HttpClientInterface;
15+
16+
class RemoteJsonManifest implements ManifestInterface
17+
{
18+
private $manifestUrl;
19+
private $httpClient;
20+
21+
public function __construct(string $manifestUrl, HttpClientInterface $httpClient)
22+
{
23+
$this->manifestUrl = $manifestUrl;
24+
$this->httpClient = $httpClient;
25+
}
26+
27+
public function getManifest(): array
28+
{
29+
return $this->httpClient->request('GET', $this->manifestUrl, [
30+
'headers' => ['accept' => 'application/json'],
31+
])->toArray();
32+
}
33+
}
+80Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset;
13+
14+
use Symfony\Component\Asset\Context\ContextInterface;
15+
use Symfony\Component\Asset\Context\NullContext;
16+
use Symfony\Component\Asset\Exception\RuntimeException;
17+
use Symfony\Component\Asset\Manifest\ManifestInterface;
18+
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
19+
20+
/**
21+
* Basic package that adds a version to asset URLs.
22+
*
23+
* @author Kris Wallsmith <kris@symfony.com>
24+
* @author Fabien Potencier <fabien@symfony.com>
25+
*/
26+
class ManifestPackage implements PackageInterface
27+
{
28+
private $manifest;
29+
private $manifestData;
30+
private $context;
31+
private $strict;
32+
33+
public function __construct(ManifestInterface $manifest, ContextInterface $context = null, bool $strict = false)
34+
{
35+
$this->manifest = $manifest;
36+
$this->context = $context ?: new NullContext();
37+
$this->strict = $strict;
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public function getVersion(string $path)
44+
{
45+
if (!isset($this->manifestData)) {
46+
$this->manifestData = $this->manifest->getManifest();
47+
}
48+
49+
if (isset($this->manifestData[$path])) {
50+
return $this->manifestData[$path];
51+
}
52+
53+
if ($this->strict) {
54+
throw new RuntimeException(sprintf('Asset "%s" not found in manifest.', $path));
55+
}
56+
57+
return $path;
58+
}
59+
60+
/**
61+
* {@inheritdoc}
62+
*/
63+
public function getUrl(string $path)
64+
{
65+
if ($this->isAbsoluteUrl($path)) {
66+
// Should be deprecated ?
67+
return $path;
68+
}
69+
70+
return $this->getVersion($path);
71+
}
72+
73+
/**
74+
* @return bool
75+
*/
76+
protected function isAbsoluteUrl(string $url)
77+
{
78+
return false !== strpos($url, '://') || '//' === substr($url, 0, 2);
79+
}
80+
}
+46Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Tests\Manifest;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Asset\Manifest\JsonManifest;
16+
use Symfony\Component\Asset\Manifest\ManifestInterface;
17+
18+
class JsonManifestTest extends TestCase
19+
{
20+
public function testGetManifest()
21+
{
22+
$manifest = $this->createManifest('manifest-valid.json');
23+
24+
$this->assertIsArray($manifest->getManifest());
25+
}
26+
27+
public function testMissingManifestFileThrowsException()
28+
{
29+
$this->expectException('RuntimeException');
30+
$manifest = $this->createManifest('non-existent-file.json');
31+
$manifest->getManifest();
32+
}
33+
34+
public function testManifestFileWithBadJSONThrowsException()
35+
{
36+
$this->expectException('RuntimeException');
37+
$this->expectExceptionMessage('Error parsing JSON');
38+
$manifest = $this->createManifest('manifest-invalid.json');
39+
$manifest->getManifest();
40+
}
41+
42+
private function createManifest($manifestFilename): ManifestInterface
43+
{
44+
return new JsonManifest(__DIR__.'/../fixtures/'.$manifestFilename);
45+
}
46+
}
+60Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\Asset\Tests\Manifest;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Asset\Manifest\ManifestInterface;
16+
use Symfony\Component\Asset\Manifest\RemoteJsonManifest;
17+
use Symfony\Component\HttpClient\Exception\JsonException;
18+
use Symfony\Component\HttpClient\MockHttpClient;
19+
use Symfony\Component\HttpClient\Response\MockResponse;
20+
21+
class RemoteJsonManifestTest extends TestCase
22+
{
23+
public function testGetManifest()
24+
{
25+
$manifest = $this->createManifest('https://cdn.example.com/manifest-valid.json');
26+
27+
$this->assertIsArray($manifest->getManifest());
28+
}
29+
30+
public function testMissingManifestFileThrowsException()
31+
{
32+
$this->expectException('RuntimeException');
33+
$this->expectExceptionMessage('HTTP 404 returned for "https://cdn.example.com/non-existent-file.json"');
34+
$manifest = $this->createManifest('https://cdn.example.com/non-existent-file.json');
35+
$manifest->getManifest();
36+
}
37+
38+
public function testManifestFileWithBadJSONThrowsException()
39+
{
40+
$this->expectException(JsonException::class);
41+
$this->expectExceptionMessage('Syntax error');
42+
$manifest = $this->createManifest('https://cdn.example.com/manifest-invalid.json');
43+
$manifest->getManifest();
44+
}
45+
46+
private function createManifest($manifestUrl): ManifestInterface
47+
{
48+
$httpClient = new MockHttpClient(function ($method, $url, $options) {
49+
$filename = __DIR__.'/../fixtures/'.basename($url);
50+
51+
if (file_exists($filename)) {
52+
return new MockResponse(file_get_contents($filename), ['http_headers' => ['content-type' => 'application/json']]);
53+
}
54+
55+
return new MockResponse('{}', ['http_code' => 404]);
56+
});
57+
58+
return new RemoteJsonManifest($manifestUrl, $httpClient);
59+
}
60+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.