8000 Create ManifestPackages to replace ManifestVersionStrategy · symfony/symfony@c3720c1 · GitHub
[go: up one dir, main page]

Skip to content

Commit c3720c1

Browse files
committed
Create ManifestPackages to replace ManifestVersionStrategy
1 parent 0000dfe commit c3720c1

14 files changed

+453
-0
lines changed
Lines 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+
}
Lines 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+
}
Lines 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+
}
Lines 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+
}
Lines 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+
}
Lines 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+
}
Lines 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+
}
Lines 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