Skip to content

Commit

Permalink
Migrate codebase from Woda namespace (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfgangschaefer authored May 22, 2020
1 parent 3e2e94f commit a448d7d
Show file tree
Hide file tree
Showing 22 changed files with 782 additions and 2 deletions.
16 changes: 14 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@
"description": "Asset manager middleware",
"license": "MIT",
"require": {
"php": "^7.4"
"php": "^7.4",
"ext-fileinfo": "*",
"ext-mbstring": "*",
"laminas/laminas-diactoros": "^2.3",
"narrowspark/mimetypes": "^1.6",
"psr/container": "^1.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"thecodingmachine/safe": "^1.1"
},
"require-dev": {
"eventjet/coding-standard": "^3.1",
"infection/infection": "^0.15.0",
"maglnet/composer-require-checker": "^2.0",
"nette/php-generator": "^3.3",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12.5",
"phpstan/phpstan-phpunit": "^0.12.6",
"phpstan/phpstan-strict-rules": "^0.12.1",
"phpunit/phpunit": "^8.5",
"thecodingmachine/phpstan-safe-rule": "^1.0",
"vimeo/psalm": "^3.8"
},
"config": {
Expand Down Expand Up @@ -44,7 +56,7 @@
"check-deps": "vendor/bin/composer-require-checker",
"cs-check": "vendor/bin/phpcs",
"cs-fix": "vendor/bin/phpcbf",
"infection": "vendor/bin/infection --threads=4 --min-msi=100 --min-covered-msi=100",
"infection": "vendor/bin/infection --threads=4 --min-msi=95 --min-covered-msi=100",
"infection-xdebug": "@composer run --timeout=0 infection -- --initial-tests-php-options='-d zend_extension=xdebug.so'",
"phpstan": "vendor/bin/phpstan analyse",
"phpunit": "vendor/bin/phpunit",
Expand Down
3 changes: 3 additions & 0 deletions infection.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"source": {
"directories": [
"src"
],
"excludes": [
"src/ConfigProvider.php"
]
},
"logs": {
Expand Down
10 changes: 10 additions & 0 deletions src/Asset/AssetFactoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Asset;

interface AssetFactoryInterface
{
public function create(string $path): AssetInterface;
}
16 changes: 16 additions & 0 deletions src/Asset/AssetInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Asset;

interface AssetInterface
{
public function getPath(): string;

public function getMimeType(): string;

public function getContentLength(): string;

public function getContent(): string;
}
48 changes: 48 additions & 0 deletions src/Asset/FileAsset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Asset;

use Narrowspark\MimeType\MimeTypeExtensionGuesser;
use SplFileInfo;

final class FileAsset implements AssetInterface
{
private ?string $content;
private string $fullPath;

public function __construct(string $fullPath)
{
$this->fullPath = $fullPath;
$this->content = null;
}

public function getPath(): string
{
return $this->fullPath;
}

public function getMimeType(): string
{
return MimeTypeExtensionGuesser::guess($this->getExtension()) ?? 'application/octet-stream';
}

public function getContent(): string
{
if ($this->content === null) {
$this->content = \Safe\file_get_contents($this->getPath());
}
return $this->content;
}

public function getContentLength(): string
{
return (string)strlen($this->getContent());
}

private function getExtension(): string
{
return (new SplFileInfo($this->getPath()))->getExtension();
}
}
13 changes: 13 additions & 0 deletions src/Asset/FileAssetFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Asset;

final class FileAssetFactory implements AssetFactoryInterface
{
public function create(string $path): AssetInterface
{
return new FileAsset($path);
}
}
32 changes: 32 additions & 0 deletions src/ConfigProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager;

use Eventjet\AssetManager\Asset\AssetFactoryInterface;
use Eventjet\AssetManager\Asset\FileAssetFactory;
use Eventjet\AssetManager\Resolver\PathMappingResolver;
use Eventjet\AssetManager\Resolver\PathMappingResolverFactory;
use Eventjet\AssetManager\Resolver\ResolverInterface;

final class ConfigProvider
{
/**
* @return array<string, mixed>
*/
public function __invoke(): array
{
return [
'dependencies' => [
'aliases' => [
AssetFactoryInterface::class => FileAssetFactory::class,
ResolverInterface::class => PathMappingResolver::class,
],
'factories' => [
PathMappingResolver::class => PathMappingResolverFactory::class,
],
],
];
}
}
29 changes: 29 additions & 0 deletions src/Middleware/ResolveAssetMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Middleware;

use Eventjet\AssetManager\Service\AssetManager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class ResolveAssetMiddleware implements MiddlewareInterface
{
private AssetManager $assetManager;

public function __construct(AssetManager $assetManager)
{
$this->assetManager = $assetManager;
}

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!$this->assetManager->resolvesToAsset($request)) {
return $handler->handle($request);
}
return $this->assetManager->buildAssetResponse($request);
}
}
39 changes: 39 additions & 0 deletions src/Resolver/PathMappingResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Resolver;

use Eventjet\AssetManager\Asset\AssetFactoryInterface;
use Eventjet\AssetManager\Asset\AssetInterface;

final class PathMappingResolver implements ResolverInterface
{
/** @var string[] */
private array $pathMapping;
private AssetFactoryInterface $factory;

/**
* @param string[] $pathMapping
*/
public function __construct(array $pathMapping, AssetFactoryInterface $factory)
{
$this->pathMapping = $pathMapping;
$this->factory = $factory;
}

public function resolve(string $path): ?AssetInterface
{
if ($path === '/') {
return null;
}
foreach ($this->pathMapping as $current) {
$fullPath = $current . $path;
if (!file_exists($fullPath)) {
continue;
}
return $this->factory->create($fullPath);
}
return null;
}
}
38 changes: 38 additions & 0 deletions src/Resolver/PathMappingResolverFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Resolver;

use Eventjet\AssetManager\Asset\AssetFactoryInterface;
use Psr\Container\ContainerInterface;
use RuntimeException;

use function count;

final class PathMappingResolverFactory
{
public function __invoke(ContainerInterface $container): PathMappingResolver
{
return new PathMappingResolver(
$this->paths($container),
$container->get(AssetFactoryInterface::class)
);
}

/**
* @return array<string>
*/
private function paths(ContainerInterface $container): array
{
/** @var array<string, mixed> $config */
$config = $container->get('config');
$paths = $config['eventjet']['asset_manager']['paths'] ?? [];
if (count($paths) < 1) {
throw new RuntimeException(
'Path mapping is missing. Please configure your path mapping at "eventjet.asset_manager.paths"'
);
}
return $paths;
}
}
12 changes: 12 additions & 0 deletions src/Resolver/ResolverInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Resolver;

use Eventjet\AssetManager\Asset\AssetInterface;

interface ResolverInterface
{
public function resolve(string $path): ?AssetInterface;
}
45 changes: 45 additions & 0 deletions src/Service/AssetManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Eventjet\AssetManager\Service;

use Eventjet\AssetManager\Resolver\ResolverInterface;
use Laminas\Diactoros\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use RuntimeException;

final class AssetManager
{
private ResolverInterface $resolver;
private StreamFactoryInterface $streamFactory;

public function __construct(ResolverInterface $resolver, StreamFactoryInterface $streamFactory)
{
$this->resolver = $resolver;
$this->streamFactory = $streamFactory;
}

public function resolvesToAsset(RequestInterface $request): bool
{
return $this->resolver->resolve($request->getUri()->getPath()) !== null;
}

public function buildAssetResponse(RequestInterface $request): ResponseInterface
{
$asset = $this->resolver->resolve($request->getUri()->getPath());
if ($asset === null) {
throw new RuntimeException(
'Asset could not be resolved. Use "resolvesToAsset" before "buildAssetResponse".'
);
}
return (new Response())
->withStatus(200)
->withAddedHeader('Content-Transfer-Encoding', 'binary')
->withAddedHeader('Content-Type', $asset->getMimeType())
->withAddedHeader('Content-Length', $asset->getContentLength())
->withBody($this->streamFactory->createStreamFromFile($asset->getPath()));
}
}
51 changes: 51 additions & 0 deletions tests/functional/Middleware/ResolveAssetMiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Eventjet\Test\Functional\AssetManager\Middleware;

use Eventjet\AssetManager\Asset\FileAssetFactory;
use Eventjet\AssetManager\Middleware\ResolveAssetMiddleware;
use Eventjet\AssetManager\Resolver\PathMappingResolver;
use Eventjet\AssetManager\Service\AssetManager;
use Eventjet\Test\Unit\AssetManager\ObjectFactory;
use Laminas\Diactoros\StreamFactory;
use PHPUnit\Framework\TestCase;

class ResolveAssetMiddlewareTest extends TestCase
{
private ResolveAssetMiddleware $middleware;

public function testHandlerIsCalledWhenAssetIsNotFound(): void
{
$called = false;
$handler = ObjectFactory::requestHandlerSpy($called);

$this->middleware->process(
ObjectFactory::serverRequest('GET', '/' . ObjectFactory::randomFileName() . '.jpg'),
$handler
);

self::assertTrue($called);
}

public function testHandlerIsNotCalledWhenAssetIsFound(): void
{
$fileName = ObjectFactory::randomFileName() . '.jpg';
$called = false;
$handler = ObjectFactory::requestHandlerSpy($called);
ObjectFactory::tmpFile('', $fileName);

$this->middleware->process(ObjectFactory::serverRequest('GET', '/' . $fileName), $handler);

self::assertFalse($called);
}

protected function setUp(): void
{
parent::setUp();
$paths = [ObjectFactory::pathToTmpFiles()];
$manager = new AssetManager(new PathMappingResolver($paths, new FileAssetFactory()), new StreamFactory());
$this->middleware = new ResolveAssetMiddleware($manager);
}
}
Loading

0 comments on commit a448d7d

Please sign in to comment.