mirror of
https://github.com/DirectoryLister/DirectoryLister.git
synced 2025-08-23 14:13:01 +02:00
@@ -10,12 +10,20 @@ jobs:
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
|
||||
services:
|
||||
- memcached
|
||||
- redis
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
- $HOME/.npm
|
||||
- app/vendor
|
||||
|
||||
before_install:
|
||||
- printf "\n" | pecl install -f apcu-5.1.18 memcached
|
||||
- echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
|
||||
install:
|
||||
- composer install --no-suggest
|
||||
- npm ci
|
||||
|
@@ -4,10 +4,12 @@ LABEL maintainer="Chris Kankiewicz <Chris@ChrisKankiewicz.com>"
|
||||
COPY .docker/apache/config/000-default.conf /etc/apache2/sites-available/000-default.conf
|
||||
COPY .docker/php/config/php.ini /usr/local/etc/php/php.ini
|
||||
|
||||
RUN apt-get update && apt-get install --assume-yes libzip-dev \
|
||||
RUN apt-get update && apt-get install --assume-yes libmemcached-dev libzip-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN docker-php-ext-install zip && pecl install xdebug && docker-php-ext-enable xdebug
|
||||
RUN docker-php-ext-install zip \
|
||||
&& pecl install memcached redis xdebug \
|
||||
&& docker-php-ext-enable memcached redis xdebug
|
||||
|
||||
RUN a2enmod rewrite
|
||||
|
||||
|
@@ -147,14 +147,6 @@ return [
|
||||
*/
|
||||
'max_hash_size' => Helpers::env('MAX_HASH_SIZE', 1000000000),
|
||||
|
||||
/**
|
||||
* Path to the view cache directory.
|
||||
* Set to 'false' to disable view caching entirely.
|
||||
*
|
||||
* Default value: 'app/cache/views'
|
||||
*/
|
||||
'view_cache' => Helpers::env('VIEW_CACHE', 'app/cache/views'),
|
||||
|
||||
/**
|
||||
* HTTP expires values.
|
||||
*
|
||||
|
71
app/config/cache.php
Normal file
71
app/config/cache.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
use App\Support\Helpers;
|
||||
|
||||
return [
|
||||
/**
|
||||
* The application cache driver. Setting this value to 'array' will disable
|
||||
* the cache across requests. Additional driver-specific options may require
|
||||
* configuration below.
|
||||
*
|
||||
* Possible values: apcu, array, file, memcached, redis, php-file
|
||||
*
|
||||
* Default value: 'file'
|
||||
*/
|
||||
'cache_driver' => Helpers::env('CACHE_DRIVER', 'file'),
|
||||
|
||||
/**
|
||||
* The app cache lifetime (in seconds). If set to 0, cache indefinitely.
|
||||
*
|
||||
* Default value: 0 (indefinitely)
|
||||
*/
|
||||
'cache_lifetime' => Helpers::env('CACHE_LIFETIME', 0),
|
||||
|
||||
/**
|
||||
* Path to the view cache directory. Set to 'false' to disable
|
||||
* view caching entirely. The view cache is separate from the application
|
||||
* cache defined above.
|
||||
*
|
||||
* Default value: 'app/cache/views'
|
||||
*/
|
||||
'view_cache' => Helpers::env('VIEW_CACHE', 'app/cache/views'),
|
||||
|
||||
/**
|
||||
* The Memcached configuration closure. This option is used when the
|
||||
* 'cache_driver' configuration option is set to 'memcached'. The closure
|
||||
* receives a Memcached object as it's only parameter. You can use this
|
||||
* object to configure the Memcached connection. At a minimum you must
|
||||
* connect to one or more Memcached servers via the 'addServer()' or
|
||||
* 'addServers()' methods.
|
||||
*
|
||||
* Reference the PHP Memcached documentation for Memcached configuration
|
||||
* options: https://secure.php.net/manual/en/book.memcached.php
|
||||
*
|
||||
* Default value: Adds a server at localhost:11211
|
||||
*/
|
||||
'memcached_config' => DI\value(function (Memcached $memcached): void {
|
||||
$memcached->addServer(
|
||||
Helpers::env('MEMCACHED_HOST', 'localhost'),
|
||||
Helpers::env('MEMCACHED_PORT', 11211)
|
||||
);
|
||||
}),
|
||||
|
||||
/**
|
||||
* The Redis configuration closure. This option is used when the
|
||||
* 'cache_driver' configuration option is set to 'redis'. The closure
|
||||
* receives a Redis object as it's only parameter. You can use this object
|
||||
* to configure the Redis connection. At a minimum you must connect to one
|
||||
* or more Redis servers via the 'connect()' or 'pconnect()' methods.
|
||||
*
|
||||
* Reference the phpredis documentation for Redis configuration options:
|
||||
* https://github.com/phpredis/phpredis#readme
|
||||
*
|
||||
* Default value: Adds a server at localhost:6379
|
||||
*/
|
||||
'redis_config' => DI\value(function (Redis $redis): void {
|
||||
$redis->pconnect(
|
||||
Helpers::env('REDIS_HOST', 'localhost'),
|
||||
Helpers::env('REDIS_PORT', 6379)
|
||||
);
|
||||
}),
|
||||
];
|
@@ -14,6 +14,7 @@ return [
|
||||
'asset_path' => DI\string('{app_path}/assets'),
|
||||
'cache_path' => DI\string('{app_path}/cache'),
|
||||
'config_path' => DI\string('{app_path}/config'),
|
||||
'source_path' => DI\string('{app_path}/src'),
|
||||
'translations_path' => DI\string('{app_path}/translations'),
|
||||
'views_path' => DI\string('{app_path}/views'),
|
||||
'icons_config' => DI\string('{config_path}/icons.php'),
|
||||
@@ -39,12 +40,6 @@ return [
|
||||
'type' => SortMethods\Type::class,
|
||||
],
|
||||
|
||||
/** Array of available translation languages */
|
||||
'translations' => [
|
||||
'de', 'en', 'es', 'fr', 'id', 'it', 'kr', 'nl',
|
||||
'pl', 'pt-BR', 'ro', 'ru', 'zh-CN', 'zh-TW'
|
||||
],
|
||||
|
||||
/** Array of view functions */
|
||||
'view_functions' => [
|
||||
ViewFunctions\Asset::class,
|
||||
@@ -62,6 +57,7 @@ return [
|
||||
|
||||
/** Container definitions */
|
||||
Symfony\Component\Finder\Finder::class => DI\factory(Factories\FinderFactory::class),
|
||||
Symfony\Contracts\Cache\CacheInterface::class => DI\Factory(Factories\CacheFactory::class),
|
||||
Symfony\Contracts\Translation\TranslatorInterface::class => DI\factory(Factories\TranslationFactory::class),
|
||||
Slim\Views\Twig::class => DI\factory(Factories\TwigFactory::class),
|
||||
Whoops\RunInterface::class => DI\create(Whoops\Run::class),
|
||||
|
@@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface;
|
||||
use Slim\Psr7\Request;
|
||||
use Slim\Psr7\Response;
|
||||
use SplFileInfo;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class FileInfoController
|
||||
@@ -14,6 +15,9 @@ class FileInfoController
|
||||
/** @var Container The application container */
|
||||
protected $container;
|
||||
|
||||
/** @var CacheInterface The application cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var TranslatorInterface Translator component */
|
||||
protected $translator;
|
||||
|
||||
@@ -21,13 +25,16 @@ class FileInfoController
|
||||
* Create a new FileInfoHandler object.
|
||||
*
|
||||
* @param \DI\Container $container
|
||||
* @param \Symfony\Contracts\Cache\CacheInterface $cache
|
||||
* @param \Symfony\Contracts\Translation\TranslatorInterface $translator
|
||||
*/
|
||||
public function __construct(
|
||||
Container $container,
|
||||
CacheInterface $cache,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->container = $container;
|
||||
$this->cache = $cache;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
@@ -55,13 +62,18 @@ class FileInfoController
|
||||
return $response->withStatus(500, $this->translator->trans('error.file_size_exceeded'));
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'hashes' => [
|
||||
'md5' => hash('md5', file_get_contents($file->getPathname())),
|
||||
'sha1' => hash('sha1', file_get_contents($file->getPathname())),
|
||||
'sha256' => hash('sha256', file_get_contents($file->getPathname())),
|
||||
]
|
||||
]));
|
||||
$response->getBody()->write($this->cache->get(
|
||||
sprintf('file-info-%s', sha1($file->getRealPath())),
|
||||
function () use ($file): string {
|
||||
return json_encode([
|
||||
'hashes' => [
|
||||
'md5' => hash_file('md5', $file->getPathname()),
|
||||
'sha1' => hash_file('sha1', $file->getPathname()),
|
||||
'sha256' => hash_file('sha256', $file->getPathname()),
|
||||
]
|
||||
]);
|
||||
}
|
||||
));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ use Slim\Psr7\Request;
|
||||
use Slim\Psr7\Response;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use ZipArchive;
|
||||
|
||||
@@ -18,6 +19,9 @@ class ZipController
|
||||
/** @var Container The application container */
|
||||
protected $container;
|
||||
|
||||
/** @var CacheInterface The application cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var Finder The Finder Component */
|
||||
protected $finder;
|
||||
|
||||
@@ -28,15 +32,18 @@ class ZipController
|
||||
* Create a new ZipHandler object.
|
||||
*
|
||||
* @param \DI\Container $container
|
||||
* @param \Symfony\Contracts\Cache\CacheInterface $cache
|
||||
* @param \PhpCsFixer\Finder $finder
|
||||
* @param \Symfony\Contracts\Translation\TranslatorInterface $translator
|
||||
*/
|
||||
public function __construct(
|
||||
Container $container,
|
||||
CacheInterface $cache,
|
||||
Finder $finder,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->container = $container;
|
||||
$this->cache = $cache;
|
||||
$this->finder = $finder;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
@@ -57,7 +64,11 @@ class ZipController
|
||||
return $response->withStatus(404, $this->translator->trans('error.file_not_found'));
|
||||
}
|
||||
|
||||
$response->getBody()->write($this->createZip($path)->getContents());
|
||||
$response->getBody()->write(
|
||||
$this->cache->get(sprintf('zip-%s', sha1($path)), function () use ($path): string {
|
||||
return $this->createZip($path)->getContents();
|
||||
})
|
||||
);
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/zip')
|
||||
->withHeader('Content-Disposition', sprintf(
|
||||
|
23
app/src/Exceptions/InvalidConfiguration.php
Normal file
23
app/src/Exceptions/InvalidConfiguration.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class InvalidConfiguration extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Createn an exception from a configiraton option and value.
|
||||
*
|
||||
* @param string $option
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromConfig(string $option, $value): self
|
||||
{
|
||||
return new static(
|
||||
sprintf("Unknown value %s for configuration option '%s'", var_export($value, true), $option)
|
||||
);
|
||||
}
|
||||
}
|
89
app/src/Factories/CacheFactory.php
Normal file
89
app/src/Factories/CacheFactory.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Factories;
|
||||
|
||||
use App\Exceptions\InvalidConfiguration;
|
||||
use DI\Container;
|
||||
use Symfony\Component\Cache\Adapter\ApcuAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
|
||||
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
class CacheFactory
|
||||
{
|
||||
/** @const Namespace for external cache drivers */
|
||||
protected const NAMESPACE_EXTERNAL = 'directory_lister';
|
||||
|
||||
/** @const Namespace for internal cache drivers */
|
||||
protected const NAMESPACE_INTERNAL = 'app';
|
||||
|
||||
/** @var Container The application container */
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Create a new CacheFactory object.
|
||||
*
|
||||
* @param \DI\Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and return a CacheInterface.
|
||||
*
|
||||
* @return \Symfony\Contracts\Cache\CacheInterface
|
||||
*/
|
||||
public function __invoke(): CacheInterface
|
||||
{
|
||||
switch ($this->container->get('cache_driver')) {
|
||||
case 'apcu':
|
||||
return new ApcuAdapter(
|
||||
self::NAMESPACE_EXTERNAL,
|
||||
$this->container->get('cache_lifetime')
|
||||
);
|
||||
|
||||
case 'array':
|
||||
return new ArrayAdapter($this->container->get('cache_lifetime'));
|
||||
|
||||
case 'file':
|
||||
return new FilesystemAdapter(
|
||||
self::NAMESPACE_INTERNAL,
|
||||
$this->container->get('cache_lifetime'),
|
||||
$this->container->get('cache_path')
|
||||
);
|
||||
|
||||
case 'memcached':
|
||||
$this->container->call('memcached_config', [$memcached = new \Memcached]);
|
||||
|
||||
return new MemcachedAdapter(
|
||||
$memcached,
|
||||
self::NAMESPACE_EXTERNAL,
|
||||
$this->container->get('cache_lifetime')
|
||||
);
|
||||
|
||||
case 'php-file':
|
||||
return new PhpFilesAdapter(
|
||||
self::NAMESPACE_INTERNAL,
|
||||
$this->container->get('cache_lifetime'),
|
||||
$this->container->get('cache_path')
|
||||
);
|
||||
|
||||
case 'redis':
|
||||
$this->container->call('redis_config', [$redis = new \Redis]);
|
||||
|
||||
return new RedisAdapter(
|
||||
$redis,
|
||||
self::NAMESPACE_EXTERNAL,
|
||||
$this->container->get('cache_lifetime')
|
||||
);
|
||||
|
||||
default:
|
||||
throw InvalidConfiguration::fromConfig('cache_driver', $this->container->get('cache_driver'));
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Factories;
|
||||
|
||||
use App\Exceptions\InvalidConfiguration;
|
||||
use Closure;
|
||||
use DI\Container;
|
||||
use PHLAK\Splat\Glob;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
@@ -46,7 +46,7 @@ class FinderFactory
|
||||
$finder->sort($sortOrder);
|
||||
} else {
|
||||
if (! array_key_exists($sortOrder, $this->container->get('sort_methods'))) {
|
||||
throw new RuntimeException("Invalid sort option '{$sortOrder}'");
|
||||
throw InvalidConfiguration::fromConfig('sort_order', $sortOrder);
|
||||
}
|
||||
|
||||
$this->container->call($this->container->get('sort_methods')[$sortOrder], [$finder]);
|
||||
|
@@ -2,10 +2,13 @@
|
||||
|
||||
namespace App\Factories;
|
||||
|
||||
use App\Exceptions\InvalidConfiguration;
|
||||
use DI\Container;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Symfony\Component\Translation\Loader\YamlFileLoader;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class TranslationFactory
|
||||
@@ -13,14 +16,18 @@ class TranslationFactory
|
||||
/** @var Container The applicaiton container */
|
||||
protected $container;
|
||||
|
||||
/** @var CacheInterface The application cache */
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Create a new TranslationFactory object.
|
||||
*
|
||||
* @param \DI\Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
public function __construct(Container $container, CacheInterface $cache)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,16 +37,17 @@ class TranslationFactory
|
||||
*/
|
||||
public function __invoke(): TranslatorInterface
|
||||
{
|
||||
$language = $this->container->get('language');
|
||||
|
||||
if (! in_array($language, $this->container->get('translations'))) {
|
||||
throw new RuntimeException("Invalid language option '{$language}'");
|
||||
if (! in_array(
|
||||
$language = $this->container->get('language'),
|
||||
$translations = $this->translations())
|
||||
) {
|
||||
throw InvalidConfiguration::fromConfig('language', $language);
|
||||
}
|
||||
|
||||
$translator = new Translator($language);
|
||||
$translator->addLoader('yaml', new YamlFileLoader());
|
||||
|
||||
foreach ($this->container->get('translations') as $language) {
|
||||
foreach ($translations as $language) {
|
||||
$translator->addResource('yaml', sprintf(
|
||||
'%s/%s.yaml', $this->container->get('translations_path'), $language
|
||||
), $language);
|
||||
@@ -47,4 +55,20 @@ class TranslationFactory
|
||||
|
||||
return $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of available translation languages.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function translations(): array
|
||||
{
|
||||
return $this->cache->get('translations', function (): array {
|
||||
return array_values(array_map(function (SplFileInfo $file): string {
|
||||
return $file->getBasename('.yaml');
|
||||
}, iterator_to_array(
|
||||
Finder::create()->in($this->container->get('translations_path'))->name('*.yaml')
|
||||
)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -40,24 +40,18 @@ class TwigFactory
|
||||
{
|
||||
$twig = new Twig(new FilesystemLoader(
|
||||
$this->container->get('views_path')
|
||||
));
|
||||
), ['cache' => $this->container->get('view_cache')]);
|
||||
|
||||
$twig->getEnvironment()->setCache(
|
||||
$this->container->get('view_cache')
|
||||
);
|
||||
$environment = $twig->getEnvironment();
|
||||
$core = $environment->getExtension(CoreExtension::class);
|
||||
|
||||
$twig->getEnvironment()->getExtension(CoreExtension::class)->setDateFormat(
|
||||
$this->container->get('date_format'), '%d days'
|
||||
);
|
||||
|
||||
$twig->getEnvironment()->getExtension(CoreExtension::class)->setTimezone(
|
||||
$this->container->get('timezone')
|
||||
);
|
||||
$core->setDateFormat($this->container->get('date_format'), '%d days');
|
||||
$core->setTimezone($this->container->get('timezone'));
|
||||
|
||||
foreach ($this->container->get('view_functions') as $function) {
|
||||
$function = $this->callableResolver->resolve($function);
|
||||
|
||||
$twig->getEnvironment()->addFunction(
|
||||
$environment->addFunction(
|
||||
new TwigFunction($function->name(), $function)
|
||||
);
|
||||
}
|
||||
|
@@ -3,12 +3,25 @@
|
||||
namespace App\ViewFunctions;
|
||||
|
||||
use ParsedownExtra;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
class Markdown extends ViewFunction
|
||||
{
|
||||
/** @var string The function name */
|
||||
protected $name = 'markdown';
|
||||
|
||||
/** @var ParsedownExtra The markdown parser */
|
||||
protected $parser;
|
||||
|
||||
/** @var CacheInterface */
|
||||
protected $cache;
|
||||
|
||||
public function __construct(ParsedownExtra $parser, CacheInterface $cache)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string of markdown into HTML.
|
||||
*
|
||||
@@ -16,8 +29,10 @@ class Markdown extends ViewFunction
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __invoke(string $string)
|
||||
public function __invoke(string $string): string
|
||||
{
|
||||
return ParsedownExtra::instance()->parse($string);
|
||||
return $this->cache->get(md5($string), function () use ($string): string {
|
||||
return $this->parser->parse($string);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@
|
||||
"slim/psr7": "^1.0",
|
||||
"slim/slim": "^4.3",
|
||||
"slim/twig-view": "^3.0",
|
||||
"symfony/cache": "^5.0",
|
||||
"symfony/finder": "^5.0",
|
||||
"symfony/translation": "^5.0",
|
||||
"symfony/yaml": "^5.0",
|
||||
@@ -39,6 +40,11 @@
|
||||
"symfony/var-dumper": "^5.0",
|
||||
"vimeo/psalm": "^3.6"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-apcu": "Required to use the APCu cache driver",
|
||||
"ext-memcached": "Required to use the Memcached driver",
|
||||
"ext-redis": "Required to use the Redis cache driver"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/src/"
|
||||
|
817
composer.lock
generated
817
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,18 @@ services:
|
||||
extra_hosts:
|
||||
- host.docker.internal:${DOCKER_HOST_IP}
|
||||
|
||||
memcached:
|
||||
container_name: directory-lister-memcached
|
||||
ports:
|
||||
- ${MEMCACHED_PORT:-11211}:11211
|
||||
image: memcached:1.6
|
||||
|
||||
redis:
|
||||
container_name: directory-lister-redis
|
||||
ports:
|
||||
- ${REDIS_PORT:-6379}:6379
|
||||
image: redis:6.0
|
||||
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
|
@@ -14,6 +14,7 @@ Dotenv::createImmutable(__DIR__)->safeLoad();
|
||||
|
||||
// Initialize the application
|
||||
$app = (new ContainerBuilder)->addDefinitions(
|
||||
__DIR__ . '/app/config/cache.php',
|
||||
__DIR__ . '/app/config/app.php',
|
||||
__DIR__ . '/app/config/container.php'
|
||||
)->build()->call(AppManager::class);
|
||||
|
@@ -1,10 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="3.11.2@d470903722cfcbc1cd04744c5491d3e6d13ec3d9">
|
||||
<files psalm-version="3.11.5@3c60609c218d4d4b3b257728b8089094e5c6c6c2">
|
||||
<file src="app/src/Controllers/DirectoryController.php">
|
||||
<PossiblyInvalidMethodCall occurrences="1">
|
||||
<code>current</code>
|
||||
</PossiblyInvalidMethodCall>
|
||||
</file>
|
||||
<file src="app/src/Factories/CacheFactory.php">
|
||||
<UndefinedFunction occurrences="2">
|
||||
<code>'memcached_config'</code>
|
||||
<code>'redis_config'</code>
|
||||
</UndefinedFunction>
|
||||
</file>
|
||||
<file src="app/src/Factories/TwigFactory.php">
|
||||
<InvalidMethodCall occurrences="1">
|
||||
<code>name</code>
|
||||
|
@@ -17,4 +17,13 @@ DATE_FORMAT="Y-m-d H:i:s"
|
||||
|
||||
MAX_HASH_SIZE=1000000000
|
||||
|
||||
CACHE_DRIVER=array
|
||||
CACHE_LIFETIME=0
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
MEMCACHED_PORT=11211
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
|
||||
VIEW_CACHE=false
|
||||
|
@@ -13,7 +13,11 @@ class FileInfoControllerTest extends TestCase
|
||||
{
|
||||
public function test_it_can_return_a_successful_response(): void
|
||||
{
|
||||
$handler = new FileInfoController($this->container, $this->container->get(TranslatorInterface::class));
|
||||
$handler = new FileInfoController(
|
||||
$this->container,
|
||||
$this->cache,
|
||||
$this->container->get(TranslatorInterface::class)
|
||||
);
|
||||
|
||||
$request = $this->createMock(Request::class);
|
||||
$request->method('getQueryParams')->willReturn(['info' => 'README.md']);
|
||||
@@ -33,7 +37,11 @@ class FileInfoControllerTest extends TestCase
|
||||
|
||||
public function test_it_can_return_a_not_found_response(): void
|
||||
{
|
||||
$handler = new FileInfoController($this->container, $this->container->get(TranslatorInterface::class));
|
||||
$handler = new FileInfoController(
|
||||
$this->container,
|
||||
$this->cache,
|
||||
$this->container->get(TranslatorInterface::class)
|
||||
);
|
||||
|
||||
$request = $this->createMock(Request::class);
|
||||
$request->method('getQueryParams')->willReturn(['info' => 'not_a_file.test']);
|
||||
@@ -47,7 +55,11 @@ class FileInfoControllerTest extends TestCase
|
||||
public function test_it_returns_an_error_when_file_size_is_too_large(): void
|
||||
{
|
||||
$this->container->set('max_hash_size', 10);
|
||||
$handler = new FileInfoController($this->container, $this->container->get(TranslatorInterface::class));
|
||||
$handler = new FileInfoController(
|
||||
$this->container,
|
||||
$this->cache,
|
||||
$this->container->get(TranslatorInterface::class)
|
||||
);
|
||||
|
||||
$request = $this->createMock(Request::class);
|
||||
$request->method('getQueryParams')->willReturn(['info' => 'README.md']);
|
||||
|
@@ -10,12 +10,13 @@ use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ZipHandlerTest extends TestCase
|
||||
class ZipControllerTest extends TestCase
|
||||
{
|
||||
public function test_it_returns_a_successful_response_for_a_zip_request(): void
|
||||
{
|
||||
$handler = new ZipController(
|
||||
$this->container,
|
||||
$this->cache,
|
||||
new Finder,
|
||||
$this->container->get(TranslatorInterface::class)
|
||||
);
|
||||
@@ -37,6 +38,7 @@ class ZipHandlerTest extends TestCase
|
||||
{
|
||||
$handler = new ZipController(
|
||||
$this->container,
|
||||
$this->cache,
|
||||
new Finder,
|
||||
$this->container->get(TranslatorInterface::class)
|
||||
);
|
||||
@@ -56,6 +58,7 @@ class ZipHandlerTest extends TestCase
|
||||
$this->container->set('zip_downloads', false);
|
||||
$handler = new ZipController(
|
||||
$this->container,
|
||||
$this->cache,
|
||||
new Finder,
|
||||
$this->container->get(TranslatorInterface::class)
|
||||
);
|
42
tests/Factories/CacheFactoryTest.php
Normal file
42
tests/Factories/CacheFactoryTest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Factories;
|
||||
|
||||
use App\Exceptions\InvalidConfiguration;
|
||||
use App\Factories\CacheFactory;
|
||||
use Symfony\Component\Cache\Adapter;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CacheFactoryTest extends TestCase
|
||||
{
|
||||
/** @dataProvider cacheAdapters */
|
||||
public function test_it_can_compose_an_adapter(string $config, string $adapter): void
|
||||
{
|
||||
$this->container->set('cache_driver', $config);
|
||||
|
||||
$cache = (new CacheFactory($this->container))();
|
||||
|
||||
$this->assertInstanceOf($adapter, $cache);
|
||||
}
|
||||
|
||||
public function test_it_throws_a_runtime_exception_with_an_invalid_sort_order(): void
|
||||
{
|
||||
$this->container->set('cache_driver', 'invalid');
|
||||
|
||||
$this->expectException(InvalidConfiguration::class);
|
||||
|
||||
(new CacheFactory($this->container))();
|
||||
}
|
||||
|
||||
public function cacheAdapters(): array
|
||||
{
|
||||
return [
|
||||
'APCu adapter' => ['apcu', Adapter\ApcuAdapter::class],
|
||||
'Array adapter' => ['array', Adapter\ArrayAdapter::class],
|
||||
'File adapter' => ['file', Adapter\FilesystemAdapter::class],
|
||||
'Memcached adapter' => ['memcached', Adapter\MemcachedAdapter::class],
|
||||
'PHP files adapter' => ['php-file', Adapter\PhpFilesAdapter::class],
|
||||
'Redis adapter' => ['redis', Adapter\RedisAdapter::class],
|
||||
];
|
||||
}
|
||||
}
|
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Tests\Factories;
|
||||
|
||||
use App\Exceptions\InvalidConfiguration;
|
||||
use App\Factories\FinderFactory;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Tests\TestCase;
|
||||
@@ -12,7 +12,7 @@ class FinderFactoryTest extends TestCase
|
||||
{
|
||||
public function test_it_can_compose_the_finder_component(): void
|
||||
{
|
||||
$finder = (new FinderFactory($this->container))();
|
||||
$finder = (new FinderFactory($this->container, $this->cache))();
|
||||
|
||||
$this->assertInstanceOf(Finder::class, $finder);
|
||||
|
||||
@@ -35,7 +35,7 @@ class FinderFactoryTest extends TestCase
|
||||
}
|
||||
));
|
||||
|
||||
$finder = (new FinderFactory($this->container))();
|
||||
$finder = (new FinderFactory($this->container, $this->cache))();
|
||||
$finder->in($this->filePath('subdir'))->depth(0);
|
||||
|
||||
$this->assertEquals([
|
||||
@@ -51,7 +51,7 @@ class FinderFactoryTest extends TestCase
|
||||
{
|
||||
$this->container->set('reverse_sort', true);
|
||||
|
||||
$finder = (new FinderFactory($this->container))();
|
||||
$finder = (new FinderFactory($this->container, $this->cache))();
|
||||
$finder->in($this->filePath('subdir'))->depth(0);
|
||||
|
||||
$this->assertEquals([
|
||||
@@ -69,7 +69,7 @@ class FinderFactoryTest extends TestCase
|
||||
'subdir/alpha.scss', 'subdir/charlie.bash', '**/*.yaml'
|
||||
]);
|
||||
|
||||
(new FinderFactory($this->container))();
|
||||
(new FinderFactory($this->container, $this->cache))();
|
||||
|
||||
$finder = $this->container->get(Finder::class);
|
||||
$finder->in($this->filePath('subdir'))->depth(0);
|
||||
@@ -85,9 +85,9 @@ class FinderFactoryTest extends TestCase
|
||||
{
|
||||
$this->container->set('sort_order', 'invalid');
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectException(InvalidConfiguration::class);
|
||||
|
||||
(new FinderFactory($this->container))();
|
||||
(new FinderFactory($this->container, $this->cache))();
|
||||
}
|
||||
|
||||
protected function getFilesArray(Finder $finder): array
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Tests\Factories;
|
||||
|
||||
use App\Exceptions\InvalidConfiguration;
|
||||
use App\Factories\TranslationFactory;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -11,7 +11,7 @@ class TranslationFactoryTest extends TestCase
|
||||
{
|
||||
public function test_it_registers_the_translation_component(): void
|
||||
{
|
||||
$translator = (new TranslationFactory($this->container))();
|
||||
$translator = (new TranslationFactory($this->container, $this->cache))();
|
||||
|
||||
$this->assertEquals('en', $translator->getLocale());
|
||||
$this->assertInstanceOf(MessageCatalogue::class, $translator->getCatalogue('de'));
|
||||
@@ -29,9 +29,9 @@ class TranslationFactoryTest extends TestCase
|
||||
|
||||
public function test_it_throws_an_exception_for_an_invalid_language(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectException(InvalidConfiguration::class);
|
||||
|
||||
$this->container->set('language', 'xx');
|
||||
(new TranslationFactory($this->container))();
|
||||
(new TranslationFactory($this->container, $this->cache))();
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,17 @@ use DI\Container;
|
||||
use DI\ContainerBuilder;
|
||||
use Dotenv\Dotenv;
|
||||
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
class TestCase extends PHPUnitTestCase
|
||||
{
|
||||
/** @var Container The test container */
|
||||
protected $container;
|
||||
|
||||
/** @var CacheInterface The test cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var string Path to test files directory */
|
||||
protected $testFilesPath = __DIR__ . '/_files';
|
||||
|
||||
@@ -25,10 +30,13 @@ class TestCase extends PHPUnitTestCase
|
||||
Dotenv::createImmutable(__DIR__)->safeLoad();
|
||||
|
||||
$this->container = (new ContainerBuilder)->addDefinitions(
|
||||
dirname(__DIR__) . '/app/config/cache.php',
|
||||
dirname(__DIR__) . '/app/config/app.php',
|
||||
dirname(__DIR__) . '/app/config/container.php'
|
||||
)->build();
|
||||
|
||||
$this->cache = new ArrayAdapter($this->container->get('cache_lifetime'));
|
||||
|
||||
$this->container->set('base_path', $this->testFilesPath);
|
||||
$this->container->set('asset_path', $this->filePath('app/assets'));
|
||||
$this->container->set('cache_path', $this->filePath('app/cache'));
|
||||
|
@@ -3,15 +3,18 @@
|
||||
namespace Tests\ViewFunctions;
|
||||
|
||||
use App\ViewFunctions\Markdown;
|
||||
use ParsedownExtra;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MarkdownTest extends TestCase
|
||||
{
|
||||
public function test_it_can_parse_markdown_into_html(): void
|
||||
{
|
||||
$markdown = new Markdown(new ParsedownExtra, $this->cache);
|
||||
|
||||
$this->assertEquals(
|
||||
'<p><strong>Test</strong> <code>markdown</code>, <del>please</del> <em>ignore</em></p>',
|
||||
(new Markdown)('**Test** `markdown`, ~~please~~ _ignore_')
|
||||
$markdown('**Test** `markdown`, ~~please~~ _ignore_')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user