diff --git a/src/Console/Command/ChecksumGenerator.php b/src/Console/Command/ChecksumGenerator.php
index 3431102..26cf881 100644
--- a/src/Console/Command/ChecksumGenerator.php
+++ b/src/Console/Command/ChecksumGenerator.php
@@ -21,6 +21,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use function basename;
+use function clearstatcache;
use function date;
use function dirname;
use function error_clear_last;
@@ -250,6 +251,7 @@ static function ($e) {
continue;
}
$hash = $printMode === 'sha1' ? $sha1Sum : $md5Sum;
+ clearstatcache(true, $realPath);
$output->writeln(
sprintf(
'[%s] %s %s',
diff --git a/src/Container/ContainerResolver.php b/src/Container/ContainerResolver.php
index 74702e7..78e3966 100644
--- a/src/Container/ContainerResolver.php
+++ b/src/Container/ContainerResolver.php
@@ -47,11 +47,10 @@ public function getContainer(): ContainerInterface
}
/**
- * @param callable|array|mixed $callable
+ * @template T of object
+ * @param callable|array|class-string|mixed $callable
* @param array $arguments
- * @return array|mixed
- * @throws ContainerFrozenException
- * @throws ContainerNotFoundException
+ * @return array|T|mixed
* @throws Throwable
*/
public function resolveCallable(mixed $callable, array $arguments = []): mixed
@@ -174,14 +173,16 @@ public function resolveArguments(
ReflectionClass|ReflectionFunctionAbstract $reflection,
$arguments
): array {
- // resolver empty arguments when auto resolve enabled
- if (!empty($arguments)) {
- return $arguments;
- }
$reflectionName = $reflection->getName();
$reflection = $reflection instanceof ReflectionClass
? $reflection->getConstructor()
: $reflection;
+
+ // resolver empty arguments when auto resolve enabled
+ /*if (!empty($arguments) && count($arguments) === $reflection->getNumberOfRequiredParameters()) {
+ return $arguments;
+ }*/
+
$parameters = $reflection?->getParameters()??[];
$container = $this->getContainer();
$containerParameters = method_exists($container, 'getParameters')
@@ -191,7 +192,9 @@ public function resolveArguments(
$containerAliases = method_exists($container, 'getAliases')
? $container->getAliases()
: [];
+ $paramArguments = [];
foreach ($parameters as $parameter) {
+ $argumentName = $parameter->getName();
$type = $parameter->getType();
if ($type instanceof ReflectionUnionType) {
foreach ($type->getTypes() as $unionType) {
@@ -214,28 +217,37 @@ public function resolveArguments(
if (!$type instanceof ReflectionNamedType
|| $type->isBuiltin()
) {
+ if (isset($arguments[$parameter->getName()])) {
+ $paramArguments[$argumentName] = $containerParameters[$parameter->getName()];
+ continue;
+ }
if (array_key_exists($parameter->getName(), $containerParameters)) {
- $arguments[] = $containerParameters[$parameter->getName()];
+ $paramArguments[$argumentName] = $containerParameters[$parameter->getName()];
continue;
}
if ($parameter->isDefaultValueAvailable()) {
- $arguments[] = $parameter->getDefaultValue();
+ $paramArguments[$argumentName] = $parameter->getDefaultValue();
continue;
}
if ($parameter->allowsNull()) {
- $arguments[] = null;
+ $paramArguments[$argumentName] = null;
continue;
}
- $arguments = [];
+ $paramArguments = [];
break;
}
+ if (isset($arguments[$parameter->getName()])) {
+ $paramArguments[$argumentName] = $arguments[$parameter->getName()];
+ continue;
+ }
+
$name = $type->getName();
if ($name === ContainerInterface::class
|| is_a($name, __CLASS__)
) {
- $arguments[] = $container->has($name)
+ $paramArguments[$argumentName] = $container->has($name)
? $container->get($name)
: $container;
continue;
@@ -244,7 +256,7 @@ public function resolveArguments(
&& isset($containerAliases[$name])
&& $container->has($containerAliases[$name])
) {
- $arguments[] = $container->get($containerAliases[$name]);
+ $paramArguments[$argumentName] = $container->get($containerAliases[$name]);
continue;
}
@@ -254,32 +266,32 @@ public function resolveArguments(
if (is_string($param) && $container->has($param)) {
$param = $container->get($param);
}
- $arguments[] = $param;
+ $paramArguments[$argumentName] = $param;
continue;
}
if ($parameter->isDefaultValueAvailable()) {
- $arguments[] = $parameter->getDefaultValue();
+ $paramArguments[$argumentName] = $parameter->getDefaultValue();
continue;
}
if ($parameter->allowsNull()) {
- $arguments[] = null;
+ $paramArguments[$argumentName] = null;
continue;
}
- $arguments = [];
+ $paramArguments = [];
break;
}
- $arguments[] = $container->get($name);
+ $paramArguments[$argumentName] = $container->get($name);
}
- if (($required = $reflection?->getNumberOfRequiredParameters()??0) > count($arguments)) {
+ if (($required = $reflection?->getNumberOfRequiredParameters()??0) > count($paramArguments)) {
throw new UnResolveAbleException(
sprintf(
'Could not resolve required arguments for : %s. Required %d argument, but %d given',
$reflectionName,
$required,
- count($arguments)
+ count($paramArguments)
)
);
}
- return $arguments;
+ return $paramArguments;
}
}
diff --git a/src/Database/Connection.php b/src/Database/Connection.php
index 7cd2ce2..c878c98 100644
--- a/src/Database/Connection.php
+++ b/src/Database/Connection.php
@@ -7,6 +7,9 @@
use ArrayAccess\TrayDigita\Container\Interfaces\ContainerIndicateInterface;
use ArrayAccess\TrayDigita\Database\Wrapper\DriverWrapper;
use ArrayAccess\TrayDigita\Database\Wrapper\EntityManagerWrapper;
+use ArrayAccess\TrayDigita\Event\Interfaces\ManagerAllocatorInterface;
+use ArrayAccess\TrayDigita\Event\Interfaces\ManagerInterface;
+use ArrayAccess\TrayDigita\Traits\Manager\ManagerAllocatorTrait;
use ArrayAccess\TrayDigita\Traits\Manager\ManagerDispatcherTrait;
use ArrayAccess\TrayDigita\Util\Filter\Consolidation;
use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper;
@@ -42,9 +45,10 @@
/**
* @mixin DoctrineConnection
*/
-class Connection implements ContainerIndicateInterface
+class Connection implements ContainerIndicateInterface, ManagerAllocatorInterface
{
- use ManagerDispatcherTrait;
+ use ManagerDispatcherTrait,
+ ManagerAllocatorTrait;
private ?DoctrineConnection $connection = null;
@@ -71,6 +75,10 @@ public function __construct(
$this->config ??= ContainerHelper::use(Config::class, $this->container)??new Config();
$this->eventManager = $eventManager??new EventManager();
$this->defaultConfiguration = $configuration??new OrmConfiguration();
+ $manager = ContainerHelper::getNull(ManagerInterface::class, $this->container);
+ if ($manager) {
+ $this->setManager($manager);
+ }
}
protected function getPrefixNameEventIdentity(): ?string
diff --git a/src/Database/Wrapper/ConnectionWrapper.php b/src/Database/Wrapper/ConnectionWrapper.php
index ea546f5..86e88f5 100644
--- a/src/Database/Wrapper/ConnectionWrapper.php
+++ b/src/Database/Wrapper/ConnectionWrapper.php
@@ -4,15 +4,17 @@
namespace ArrayAccess\TrayDigita\Database\Wrapper;
use ArrayAccess\TrayDigita\Database\Connection;
+use ArrayAccess\TrayDigita\Event\Interfaces\ManagerIndicateInterface;
use ArrayAccess\TrayDigita\Event\Interfaces\ManagerInterface;
use ArrayAccess\TrayDigita\Traits\Manager\ManagerDispatcherTrait;
+use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper;
use Doctrine\DBAL\Driver\Connection as DoctrineConnection;
use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Throwable;
-class ConnectionWrapper extends AbstractConnectionMiddleware
+class ConnectionWrapper extends AbstractConnectionMiddleware implements ManagerIndicateInterface
{
use ManagerDispatcherTrait;
@@ -28,19 +30,13 @@ protected function getPrefixNameEventIdentity(): ?string
return 'connection';
}
- protected function getManagerFromContainer(): ?ManagerInterface
+
+ public function getManager(): ?ManagerInterface
{
- $container = $this->databaseConnection->getContainer();
- try {
- $manager = $container->has(ManagerInterface::class)
- ? $container->get(ManagerInterface::class)
- : null;
- } catch (Throwable) {
- $manager = null;
- }
- return $manager instanceof ManagerInterface ? $manager : null;
+ return $this->databaseConnection->getManager();
}
+
public function prepare(string $sql): Statement
{
// @dispatch(connection.queryString)
@@ -56,7 +52,10 @@ public function prepare(string $sql): Statement
$this->databaseConnection
);
- $prepared = parent::prepare($sql);
+ $prepared = new StatementWrapper(
+ $this,
+ parent::prepare($sql)
+ );
// @dispatch(connection.prepare)
$this->dispatchCurrent(
diff --git a/src/Database/Wrapper/DriverWrapper.php b/src/Database/Wrapper/DriverWrapper.php
index c026460..81a4689 100644
--- a/src/Database/Wrapper/DriverWrapper.php
+++ b/src/Database/Wrapper/DriverWrapper.php
@@ -4,7 +4,9 @@
namespace ArrayAccess\TrayDigita\Database\Wrapper;
use ArrayAccess\TrayDigita\Database\Connection;
+use ArrayAccess\TrayDigita\Event\Interfaces\ManagerIndicateInterface;
use ArrayAccess\TrayDigita\Event\Interfaces\ManagerInterface;
+use ArrayAccess\TrayDigita\Traits\Manager\ManagerDispatcherTrait;
use ArrayAccess\TrayDigita\Traits\Service\CallStackTraceTrait;
use ArrayAccess\TrayDigita\Util\Filter\Conversion;
use DateTimeZone;
@@ -21,9 +23,10 @@
use function preg_match;
use function trim;
-final class DriverWrapper extends AbstractDriverMiddleware
+final class DriverWrapper extends AbstractDriverMiddleware implements ManagerIndicateInterface
{
- use CallStackTraceTrait;
+ use CallStackTraceTrait,
+ ManagerDispatcherTrait;
public function __construct(
private readonly Connection $databaseConnection,
@@ -32,32 +35,27 @@ public function __construct(
parent::__construct($wrappedDriver);
}
+ protected function getPrefixNameEventIdentity(): ?string
+ {
+ return 'connection';
+ }
+
public function getDatabaseConnection(): Connection
{
return $this->databaseConnection;
}
+ public function getManager(): ?ManagerInterface
+ {
+ return $this->databaseConnection->getManager();
+ }
+
public function connect(#[SensitiveParameter] array $params) : DoctrineConnection
{
$this->assertCallstack();
- $container = $this->databaseConnection->getContainer();
- try {
- $manager = $container->has(ManagerInterface::class)
- ? $container->get(ManagerInterface::class)
- : null;
- } catch (Throwable) {
- $manager = null;
- }
- $manager = $manager instanceof ManagerInterface ? $manager : null;
-
// @dispatch(connection.beforeConnect)
- $manager?->dispatch(
- 'connection.beforeConnect',
- $params,
- $this->databaseConnection,
- $this,
- );
+ $this->dispatchBefore($params, $this->databaseConnection);
try {
$connection = new ConnectionWrapper(
@@ -67,8 +65,7 @@ public function connect(#[SensitiveParameter] array $params) : DoctrineConnectio
$this->initConnection($connection);
// @dispatch(connection.connect)
- $manager?->dispatch(
- 'connection.connect',
+ $this->dispatchCurrent(
$params,
$this->databaseConnection,
$this,
@@ -78,8 +75,7 @@ public function connect(#[SensitiveParameter] array $params) : DoctrineConnectio
} finally {
$this->resetCallstack();
// @dispatch(connection.afterConnect)
- $manager?->dispatch(
- 'connection.afterConnect',
+ $this->dispatchAfter(
$params,
$this->databaseConnection,
$this,
@@ -90,6 +86,8 @@ public function connect(#[SensitiveParameter] array $params) : DoctrineConnectio
private function initConnection(Driver\Connection $connection): void
{
+ // @dispatch(connection.beforeInitConnection)
+ $this->dispatchBefore($connection, $this->databaseConnection);
$platform = $this->wrappedDriver->getDatabasePlatform();
$query = '';
$config = $this->databaseConnection->getDatabaseConfig();
@@ -148,8 +146,25 @@ private function initConnection(Driver\Connection $connection): void
$query = "SET SESSION TIME_ZONE='$timezone';";
}
try {
- $query && $connection->exec($query);
+ if ($query) {
+ $result = $connection->exec($query);
+ }
+ // @dispatch(connection.initConnection)
+ $this->dispatchCurrent(
+ $connection,
+ $this->databaseConnection,
+ $query,
+ $result??null
+ );
} catch (Throwable) {
+ } finally {
+ // @dispatch(connection.afterInitConnection)
+ $this->dispatchAfter(
+ $connection,
+ $this->databaseConnection,
+ $query,
+ $result??null
+ );
}
}
}
diff --git a/src/Database/Wrapper/EntityManagerWrapper.php b/src/Database/Wrapper/EntityManagerWrapper.php
index 6480487..c5780ee 100644
--- a/src/Database/Wrapper/EntityManagerWrapper.php
+++ b/src/Database/Wrapper/EntityManagerWrapper.php
@@ -4,6 +4,7 @@
namespace ArrayAccess\TrayDigita\Database\Wrapper;
use ArrayAccess\TrayDigita\Database\Connection;
+use ArrayAccess\TrayDigita\Event\Interfaces\ManagerIndicateInterface;
use ArrayAccess\TrayDigita\Event\Interfaces\ManagerInterface;
use ArrayAccess\TrayDigita\Traits\Manager\ManagerDispatcherTrait;
use Doctrine\Common\Collections\Selectable;
@@ -17,7 +18,7 @@
use Throwable;
use function get_object_vars;
-class EntityManagerWrapper extends EntityManagerDecorator
+class EntityManagerWrapper extends EntityManagerDecorator implements ManagerIndicateInterface
{
use ManagerDispatcherTrait;
@@ -89,17 +90,9 @@ private function injectEntityManager(
}
}
- protected function getManagerFromContainer() : ?ManagerInterface
+ public function getManager(): ?ManagerInterface
{
- $container = $this->databaseConnection->getContainer();
- try {
- $manager = $container->has(ManagerInterface::class)
- ? $container->get(ManagerInterface::class)
- : null;
- } catch (Throwable) {
- $manager = null;
- }
- return $manager instanceof ManagerInterface ? $manager : null;
+ return $this->databaseConnection->getManager();
}
/**
diff --git a/src/Database/Wrapper/EntityRepositoryWrapper.php b/src/Database/Wrapper/EntityRepositoryWrapper.php
index fcb0b82..0696e62 100644
--- a/src/Database/Wrapper/EntityRepositoryWrapper.php
+++ b/src/Database/Wrapper/EntityRepositoryWrapper.php
@@ -44,17 +44,9 @@ public function getRepository(): EntityRepository|ObjectRepository
return $this->repository;
}
- protected function getManagerFromContainer(): ?ManagerInterface
+ public function getManager(): ?ManagerInterface
{
- $container = $this->databaseConnection->getContainer();
- try {
- $manager = $container->has(ManagerInterface::class)
- ? $container->get(ManagerInterface::class)
- : null;
- } catch (Throwable) {
- $manager = null;
- }
- return $manager instanceof ManagerInterface ? $manager : null;
+ return $this->databaseConnection->getManager();
}
public function find($id, $lockMode = null, $lockVersion = null)
diff --git a/src/Database/Wrapper/ResultWrapper.php b/src/Database/Wrapper/ResultWrapper.php
new file mode 100644
index 0000000..c339e17
--- /dev/null
+++ b/src/Database/Wrapper/ResultWrapper.php
@@ -0,0 +1,99 @@
+statementWrapper->getManager();
+ }
+
+ public function fetchNumeric()
+ {
+ return $this->dispatchWrap(
+ fn () => parent::fetchNumeric(),
+ $this->statementWrapper
+ );
+ }
+
+ public function fetchAssociative()
+ {
+ return $this->dispatchWrap(
+ fn () => parent::fetchAssociative(),
+ $this->statementWrapper
+ );
+ }
+
+ public function fetchOne()
+ {
+ return $this->dispatchWrap(
+ fn () => parent::fetchOne(),
+ $this->statementWrapper
+ );
+ }
+
+ public function fetchAllNumeric(): array
+ {
+ return $this->dispatchWrap(
+ fn () => parent::fetchAllNumeric(),
+ $this->statementWrapper
+ );
+ }
+
+ public function fetchAllAssociative(): array
+ {
+ return $this->dispatchWrap(
+ fn () => parent::fetchAllAssociative(),
+ $this->statementWrapper
+ );
+ }
+
+ public function fetchFirstColumn(): array
+ {
+ return $this->dispatchWrap(
+ fn () => parent::fetchFirstColumn(),
+ $this->statementWrapper
+ );
+ }
+
+ public function rowCount(): int
+ {
+ return $this->dispatchWrap(
+ fn () => parent::rowCount(),
+ $this->statementWrapper
+ );
+ }
+
+ public function columnCount(): int
+ {
+ return $this->dispatchWrap(
+ fn () => parent::columnCount(),
+ $this->statementWrapper
+ );
+ }
+
+ public function free(): void
+ {
+ $this->dispatchWrap(
+ fn () => parent::free(),
+ $this->statementWrapper
+ );
+ }
+}
diff --git a/src/Database/Wrapper/StatementWrapper.php b/src/Database/Wrapper/StatementWrapper.php
new file mode 100644
index 0000000..93b4148
--- /dev/null
+++ b/src/Database/Wrapper/StatementWrapper.php
@@ -0,0 +1,42 @@
+connectionWrapper->getManager();
+ }
+
+ public function execute($params = null): Result
+ {
+ return $this->dispatchWrap(
+ fn () => parent::execute($params),
+ $params,
+ $this->connectionWrapper
+ );
+ }
+}
diff --git a/src/Lang/default.pot b/src/Lang/default.pot
index a514121..142c753 100644
--- a/src/Lang/default.pot
+++ b/src/Lang/default.pot
@@ -2,7 +2,7 @@
msgid ""
msgstr ""
"Project-Id-Version: TrayDigita 1.0.0\n"
-"POT-Creation-Date: 2023-10-19 03:54+0700\n"
+"POT-Creation-Date: 2023-10-19 21:20+0700\n"
"PO-Revision-Date: 2023-09-24 19:00+0700\n"
"Last-Translator: ArrayAccess\n"
"Language-Team: ArrayAccess\n"
@@ -48,21 +48,21 @@ msgctxt "benchmark"
msgid "Service"
msgstr ""
-#: Benchmark/Middlewares/DebuggingMiddleware.php:201
-#: Benchmark/Middlewares/DebuggingMiddleware.php:205
-#: Benchmark/Middlewares/DebuggingMiddleware.php:255
+#: Benchmark/Middlewares/DebuggingMiddleware.php:212
+#: Benchmark/Middlewares/DebuggingMiddleware.php:216
+#: Benchmark/Middlewares/DebuggingMiddleware.php:267
msgctxt "benchmark"
msgid "rendered time"
msgstr ""
-#: Benchmark/Middlewares/DebuggingMiddleware.php:203
-#: Benchmark/Middlewares/DebuggingMiddleware.php:207
-#: Benchmark/Middlewares/DebuggingMiddleware.php:262
+#: Benchmark/Middlewares/DebuggingMiddleware.php:214
+#: Benchmark/Middlewares/DebuggingMiddleware.php:218
+#: Benchmark/Middlewares/DebuggingMiddleware.php:274
msgctxt "benchmark"
msgid "memory usage"
msgstr ""
-#: Benchmark/Middlewares/DebuggingMiddleware.php:260
+#: Benchmark/Middlewares/DebuggingMiddleware.php:272
msgctxt "benchmark"
msgid "peak memory usage"
msgstr ""
@@ -611,29 +611,29 @@ msgctxt "console"
msgid "Press %s to exit."
msgstr ""
-#: Console/Command/ChecksumGenerator.php:60
+#: Console/Command/ChecksumGenerator.php:61
msgctxt "console"
msgid "Create list of core file checksums."
msgstr ""
-#: Console/Command/ChecksumGenerator.php:69
+#: Console/Command/ChecksumGenerator.php:70
msgctxt "console"
msgid "Display checksums on terminal without writing to disk"
msgstr ""
-#: Console/Command/ChecksumGenerator.php:79
+#: Console/Command/ChecksumGenerator.php:80
#, php-format
msgctxt "console"
msgid "The %s creating checksum files on %s directory."
msgstr ""
-#: Console/Command/ChecksumGenerator.php:121
+#: Console/Command/ChecksumGenerator.php:122
#, php-format
msgctxt "console"
msgid "Files will be put in directory: %s"
msgstr ""
-#: Console/Command/ChecksumGenerator.php:133
+#: Console/Command/ChecksumGenerator.php:134
#: Console/Command/CommandGenerator.php:333
#: Console/Command/ControllerGenerator.php:296
#: Console/Command/DatabaseChecker.php:1255
@@ -647,7 +647,7 @@ msgctxt "console"
msgid "Are you sure to continue (Yes/No)?"
msgstr ""
-#: Console/Command/ChecksumGenerator.php:148
+#: Console/Command/ChecksumGenerator.php:149
#: Console/Command/CommandGenerator.php:346
#: Console/Command/ControllerGenerator.php:309
#: Console/Command/DatabaseChecker.php:1270
@@ -661,7 +661,7 @@ msgctxt "console"
msgid "Please enter valid answer! (Yes / No)"
msgstr ""
-#: Console/Command/ChecksumGenerator.php:160
+#: Console/Command/ChecksumGenerator.php:161
#: Console/Command/CommandGenerator.php:390
#: Console/Command/ControllerGenerator.php:351
#: Console/Command/DatabaseChecker.php:1282
@@ -675,12 +675,12 @@ msgctxt "console"
msgid "Operation cancelled!"
msgstr ""
-#: Console/Command/ChecksumGenerator.php:271
+#: Console/Command/ChecksumGenerator.php:273
msgctxt "console"
msgid "Done!"
msgstr ""
-#: Console/Command/ChecksumGenerator.php:283
+#: Console/Command/ChecksumGenerator.php:285
msgctxt "console"
msgid "Source"
msgstr ""
@@ -1887,3 +1887,128 @@ msgstr ""
msgctxt "template"
msgid "unknown"
msgstr ""
+
+#: Uploader/Chunk.php:132
+msgctxt "chunk-uploader"
+msgid "Storage directory could not be empty or whitespace only."
+msgstr ""
+
+#: Uploader/Chunk.php:142
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Directory %s is not exist"
+msgstr ""
+
+#: Uploader/Chunk.php:154
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Directory %s is not writable"
+msgstr ""
+
+#: Uploader/ChunkHandler.php:116 Uploader/ChunkProcessor.php:223
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Request id \"%s\" is not valid"
+msgstr ""
+
+#: Uploader/ChunkHandler.php:224
+msgctxt "chunk-uploader"
+msgid "Cache upload storage is not writable."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:276
+msgctxt "chunk-uploader"
+msgid "Upload cache file is not writable."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:290
+msgctxt "chunk-uploader"
+msgid "Can not create cached stream."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:301
+msgctxt "chunk-uploader"
+msgid "Cache file has been locked."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:395
+msgctxt "chunk-uploader"
+msgid "Offset upload position is invalid."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:427
+msgctxt "chunk-uploader"
+msgid "Source uploaded file does not exist."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:447
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Upload cache file is not ready to move : (%d)."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:496
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Target file \"%s\" is not writable."
+msgstr ""
+
+#: Uploader/ChunkHandler.php:516
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Target directory \"%s\" is not writable."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:46
+msgctxt "chunk-uploader"
+msgid "Uploaded files does not contain file name."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:137
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Content-Range \"%s\" is invalid"
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:152
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Content-Range unit \"%s\" is invalid"
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:163
+msgctxt "chunk-uploader"
+msgid "System does not support unknown size"
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:184
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Uploaded file size is bigger than allowed size: %s."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:200
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Uploaded file size range is less than allowed minimum size: %s."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:214
+msgctxt "chunk-uploader"
+msgid "Content-Range start bytes must be zero without Request-Id."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:234
+msgctxt "chunk-uploader"
+msgid "Content-Range start bytes must be greater zero with Request-Id."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:246
+msgctxt "chunk-uploader"
+msgid "Uploaded file size is bigger than ending size."
+msgstr ""
+
+#: Uploader/ChunkProcessor.php:255
+msgctxt "chunk-uploader"
+msgid "Range size is bigger than file size."
+msgstr ""
diff --git a/src/Lang/id.po b/src/Lang/id.po
index 89067b9..08dbda1 100644
--- a/src/Lang/id.po
+++ b/src/Lang/id.po
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: TrayDigita 1.0.0\n"
-"POT-Creation-Date: 2023-10-19 03:54+0700\n"
-"PO-Revision-Date: 2023-10-19 03:55+0700\n"
+"POT-Creation-Date: 2023-10-19 21:21+0700\n"
+"PO-Revision-Date: 2023-10-19 21:21+0700\n"
"Last-Translator: \n"
"Language-Team: ArrayAccess\n"
"Language: id\n"
@@ -47,21 +47,21 @@ msgctxt "benchmark"
msgid "Service"
msgstr "Layanan"
-#: Benchmark/Middlewares/DebuggingMiddleware.php:201
-#: Benchmark/Middlewares/DebuggingMiddleware.php:205
-#: Benchmark/Middlewares/DebuggingMiddleware.php:255
+#: Benchmark/Middlewares/DebuggingMiddleware.php:212
+#: Benchmark/Middlewares/DebuggingMiddleware.php:216
+#: Benchmark/Middlewares/DebuggingMiddleware.php:267
msgctxt "benchmark"
msgid "rendered time"
msgstr "waktu pemuatan"
-#: Benchmark/Middlewares/DebuggingMiddleware.php:203
-#: Benchmark/Middlewares/DebuggingMiddleware.php:207
-#: Benchmark/Middlewares/DebuggingMiddleware.php:262
+#: Benchmark/Middlewares/DebuggingMiddleware.php:214
+#: Benchmark/Middlewares/DebuggingMiddleware.php:218
+#: Benchmark/Middlewares/DebuggingMiddleware.php:274
msgctxt "benchmark"
msgid "memory usage"
msgstr "penggunaan memori"
-#: Benchmark/Middlewares/DebuggingMiddleware.php:260
+#: Benchmark/Middlewares/DebuggingMiddleware.php:272
msgctxt "benchmark"
msgid "peak memory usage"
msgstr "penggunaan puncak memori"
@@ -621,29 +621,29 @@ msgctxt "console"
msgid "Press %s to exit."
msgstr "Tekan %s untuk keluar."
-#: Console/Command/ChecksumGenerator.php:60
+#: Console/Command/ChecksumGenerator.php:61
msgctxt "console"
msgid "Create list of core file checksums."
msgstr "Membuat berkas daftar inti ceksum."
-#: Console/Command/ChecksumGenerator.php:69
+#: Console/Command/ChecksumGenerator.php:70
msgctxt "console"
msgid "Display checksums on terminal without writing to disk"
msgstr "Tampilkan checksum di terminal tanpa menulis ke disk"
-#: Console/Command/ChecksumGenerator.php:79
+#: Console/Command/ChecksumGenerator.php:80
#, php-format
msgctxt "console"
msgid "The %s creating checksum files on %s directory."
msgstr "%s membuat berkas ceksum pada direktori %s."
-#: Console/Command/ChecksumGenerator.php:121
+#: Console/Command/ChecksumGenerator.php:122
#, php-format
msgctxt "console"
msgid "Files will be put in directory: %s"
msgstr "Berkas akan diletakkan pada direktori: %s"
-#: Console/Command/ChecksumGenerator.php:133
+#: Console/Command/ChecksumGenerator.php:134
#: Console/Command/CommandGenerator.php:333
#: Console/Command/ControllerGenerator.php:296
#: Console/Command/DatabaseChecker.php:1255
@@ -657,7 +657,7 @@ msgctxt "console"
msgid "Are you sure to continue (Yes/No)?"
msgstr "Apakah anda yakin ingin melanjutkan (Yes/No)?"
-#: Console/Command/ChecksumGenerator.php:148
+#: Console/Command/ChecksumGenerator.php:149
#: Console/Command/CommandGenerator.php:346
#: Console/Command/ControllerGenerator.php:309
#: Console/Command/DatabaseChecker.php:1270
@@ -671,7 +671,7 @@ msgctxt "console"
msgid "Please enter valid answer! (Yes / No)"
msgstr "Mohon masukkan jawaban yang benar! (Yes/No)"
-#: Console/Command/ChecksumGenerator.php:160
+#: Console/Command/ChecksumGenerator.php:161
#: Console/Command/CommandGenerator.php:390
#: Console/Command/ControllerGenerator.php:351
#: Console/Command/DatabaseChecker.php:1282
@@ -685,12 +685,12 @@ msgctxt "console"
msgid "Operation cancelled!"
msgstr "Operasi dibatalkan!"
-#: Console/Command/ChecksumGenerator.php:271
+#: Console/Command/ChecksumGenerator.php:273
msgctxt "console"
msgid "Done!"
msgstr "Selesai!"
-#: Console/Command/ChecksumGenerator.php:283
+#: Console/Command/ChecksumGenerator.php:285
msgctxt "console"
msgid "Source"
msgstr "Sumber"
@@ -1956,6 +1956,134 @@ msgctxt "template"
msgid "unknown"
msgstr "tak diketahui"
+#: Uploader/Chunk.php:132
+msgctxt "chunk-uploader"
+msgid "Storage directory could not be empty or whitespace only."
+msgstr "Direktori penyimpanan tidak dapat kosong atau berupa whitespace saja."
+
+#: Uploader/Chunk.php:142
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Directory %s is not exist"
+msgstr "Direktori %s tidak ada"
+
+#: Uploader/Chunk.php:154
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Directory %s is not writable"
+msgstr "Direktori %s tidak dapat ditulisi"
+
+#: Uploader/ChunkHandler.php:116 Uploader/ChunkProcessor.php:223
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Request id \"%s\" is not valid"
+msgstr "Request id \"%s\" tidak sah"
+
+#: Uploader/ChunkHandler.php:224
+msgctxt "chunk-uploader"
+msgid "Cache upload storage is not writable."
+msgstr "Direktori penyimpanan cache tidak dapat ditulisi."
+
+#: Uploader/ChunkHandler.php:276
+msgctxt "chunk-uploader"
+msgid "Upload cache file is not writable."
+msgstr "Berkas cache unggahan tidak dapat ditulisi."
+
+#: Uploader/ChunkHandler.php:290
+msgctxt "chunk-uploader"
+msgid "Can not create cached stream."
+msgstr "Tidak dapat membuat cached stream."
+
+#: Uploader/ChunkHandler.php:301
+msgctxt "chunk-uploader"
+msgid "Cache file has been locked."
+msgstr "Berkas cache terkunci."
+
+#: Uploader/ChunkHandler.php:395
+msgctxt "chunk-uploader"
+msgid "Offset upload position is invalid."
+msgstr "Posisi offset unggahan tidak sah."
+
+#: Uploader/ChunkHandler.php:427
+msgctxt "chunk-uploader"
+msgid "Source uploaded file does not exist."
+msgstr "Berkas sumber yang diunggah tidak ada."
+
+#: Uploader/ChunkHandler.php:447
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Upload cache file is not ready to move : (%d)."
+msgstr "Berkas cache yang diunggah belum siap dipindahkan : (%d)."
+
+#: Uploader/ChunkHandler.php:496
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Target file \"%s\" is not writable."
+msgstr "Berkas tujuan \"%s\" tidak dapat ditulisi."
+
+#: Uploader/ChunkHandler.php:516
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Target directory \"%s\" is not writable."
+msgstr "Direktori tujuan \"%s\" tidak dapat ditulisi."
+
+#: Uploader/ChunkProcessor.php:46
+msgctxt "chunk-uploader"
+msgid "Uploaded files does not contain file name."
+msgstr "Berkas terunggah tidak mempunyai nama berkas."
+
+#: Uploader/ChunkProcessor.php:137
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Content-Range \"%s\" is invalid"
+msgstr "Content-Range \"%s\" tidak sah"
+
+#: Uploader/ChunkProcessor.php:152
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Content-Range unit \"%s\" is invalid"
+msgstr "Unit Content-Range \"%s\" tidak sah"
+
+#: Uploader/ChunkProcessor.php:163
+msgctxt "chunk-uploader"
+msgid "System does not support unknown size"
+msgstr "Sistem tidak mendukung ukuran yang tidak diketahui"
+
+#: Uploader/ChunkProcessor.php:184
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Uploaded file size is bigger than allowed size: %s."
+msgstr ""
+"Ukuran berkas yang diunggah lebih besar dari ukuran yang diperbolehkan: %s."
+
+#: Uploader/ChunkProcessor.php:200
+#, php-format
+msgctxt "chunk-uploader"
+msgid "Uploaded file size range is less than allowed minimum size: %s."
+msgstr ""
+"Ukuran rentang berkas yang diunggah kurang dari ukuran minimum yang "
+"diperbolehkan: %s."
+
+#: Uploader/ChunkProcessor.php:214
+msgctxt "chunk-uploader"
+msgid "Content-Range start bytes must be zero without Request-Id."
+msgstr "Content-Range byte awal harus nol atau tanpa Request-Id."
+
+#: Uploader/ChunkProcessor.php:234
+msgctxt "chunk-uploader"
+msgid "Content-Range start bytes must be greater zero with Request-Id."
+msgstr "Content-Range awal harus lebih besar dari nol dengan Id Permintaan."
+
+#: Uploader/ChunkProcessor.php:246
+msgctxt "chunk-uploader"
+msgid "Uploaded file size is bigger than ending size."
+msgstr "Ukuran berkas yang diunggah lebih besar dari ukuran akhir."
+
+#: Uploader/ChunkProcessor.php:255
+msgctxt "chunk-uploader"
+msgid "Range size is bigger than file size."
+msgstr "Ukuran rentang lebih besar dari ukuran berkas."
+
#~ msgctxt "console"
#~ msgid "No task in queue"
#~ msgstr "Tidak ada tugas dalam antrian"
diff --git a/src/Traits/Manager/ManagerDispatcherTrait.php b/src/Traits/Manager/ManagerDispatcherTrait.php
index 26bc7f1..244e151 100644
--- a/src/Traits/Manager/ManagerDispatcherTrait.php
+++ b/src/Traits/Manager/ManagerDispatcherTrait.php
@@ -24,65 +24,13 @@ protected function getPrefixNameEventIdentity(): ?string
return null;
}
- protected function getManagerFromContainer(): ?ManagerInterface
- {
- return ContainerHelper::getNull(ManagerInterface::class, $this->getContainer());
- }
-
- protected function getInternalMethodManager() : ?ManagerInterface
- {
- if ($this->internalMethodManager !== null) {
- $method = $this->internalMethodManager?:null;
- return $method ? $this->$method() : null;
- }
-
- if ($this instanceof ManagerIndicateInterface) {
- $this->internalMethodManager = 'getManager';
- return $this->getManager();
- }
-
- $manager = $this->getManagerFromContainer();
- if ($manager) {
- $this->internalMethodManager = 'getManagerFromContainer';
- return $manager;
- }
-
- $ref = new ReflectionObject($this);
- if (!$ref->hasMethod('getManager')) {
- return null;
- }
- $method = $ref->getMethod('getManager');
- $returnType = $method->getNumberOfRequiredParameters() === 0
- ? $method->getReturnType()
- : null;
- if (!$returnType) {
- return null;
- }
- $types = [];
- if ($returnType instanceof ReflectionNamedType) {
- $types = [$returnType];
- } elseif ($returnType instanceof ReflectionUnionType) {
- $types = $returnType->getTypes();
- }
- foreach ($types as $type) {
- if ($type->isBuiltin()) {
- continue;
- }
- if ($type->getName() === ManagerInterface::class
- || is_subclass_of($type->getName(), ManagerInterface::class)
- ) {
- $this->internalMethodManager = $type->getName();
- return $this->{$this->internalMethodManager}();
- }
- }
- return null;
- }
+ abstract public function getManager() : ?ManagerInterface;
protected function dispatchEvent(
string $eventName,
...$arguments
) {
- $manager = $this->getInternalMethodManager();
+ $manager = $this->getManager();
if (!$manager) {
return count($arguments) > 0
? reset($arguments)
@@ -133,4 +81,16 @@ protected function dispatchAfter(...$arguments)
{
return $this->doDispatchMethod('after', ...$arguments);
}
+
+ protected function dispatchWrap(callable $callback, ...$arguments)
+ {
+ try {
+ $this->doDispatchMethod('before', ...$arguments);
+ $arguments[] = $callback();
+ $this->doDispatchMethod(null, ...$arguments);
+ return end($arguments);
+ } finally {
+ $this->doDispatchMethod('after', ...$arguments);
+ }
+ }
}
diff --git a/src/Uploader/Chunk.php b/src/Uploader/Chunk.php
index 2e58603..9a649ec 100644
--- a/src/Uploader/Chunk.php
+++ b/src/Uploader/Chunk.php
@@ -12,6 +12,7 @@
use ArrayAccess\TrayDigita\Traits\Service\TranslatorTrait;
use ArrayAccess\TrayDigita\Uploader\Exceptions\DirectoryUnWritAbleException;
use ArrayAccess\TrayDigita\Uploader\Exceptions\NotExistsDirectoryException;
+use ArrayAccess\TrayDigita\Util\Filter\Consolidation;
use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper;
use DirectoryIterator;
use Psr\Container\ContainerInterface;
@@ -19,8 +20,11 @@
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use function is_dir;
+use function is_int;
use function is_string;
use function is_writable;
+use function max;
+use function min;
use function realpath;
use function sprintf;
use function sys_get_temp_dir;
@@ -38,6 +42,10 @@ class Chunk implements ManagerAllocatorInterface, ContainerIndicateInterface
*/
const MAX_AGE_FILE = 18000;
+ const MIN_FILE_SIZE = 1024;
+
+ const DEFAULT_MIN_FILE_SIZE = 512000;
+
/**
* Maximum size for unlink
*/
@@ -53,6 +61,11 @@ class Chunk implements ManagerAllocatorInterface, ContainerIndicateInterface
*/
public readonly string $partialExtension;
+ /**
+ * @var string
+ */
+ public readonly string $partialMetaExtension;
+
/**
* @var int
*/
@@ -60,10 +73,18 @@ class Chunk implements ManagerAllocatorInterface, ContainerIndicateInterface
/**
* @var ?int $limitMaxFileSize total max file size null as unlimited
- * default : 134217728 bytes or 128MB
+ * default : 134217728 bytes or 128MiB
*/
private ?int $limitMaxFileSize = 134217728;
+ /**
+ * @var ?int minimum size limit null as unlimited
+ * default: 512000 as 500Kib
+ */
+ private ?int $limitMinimumFileSize = self::DEFAULT_MIN_FILE_SIZE;
+
+ private int $maxUploadFileSize;
+
public function __construct(
protected ContainerInterface $container,
?string $storageDir = null
@@ -88,12 +109,14 @@ public function __construct(
$this->setManager($manager);
}
$this->partialExtension = 'partial';
+ $this->partialMetaExtension = $this->partialExtension . '.meta';
$storageDir = $storageDir??sys_get_temp_dir();
$this->assertDirectory($storageDir);
$storageDir = (realpath($storageDir)??$storageDir);
$this->uploadCacheStorageDirectory = $storageDir
. DIRECTORY_SEPARATOR
. self::SUFFIX_STORAGE_DIRECTORY;
+ $this->maxUploadFileSize = Consolidation::getMaxUploadSize();
}
public function getContainer(): ?ContainerInterface
@@ -105,14 +128,20 @@ private function assertDirectory(string $directory): void
{
if (trim($directory) === '') {
throw new EmptyArgumentException(
- 'Storage directory could not be empty or whitespace only.'
+ $this->translateContext(
+ 'Storage directory could not be empty or whitespace only.',
+ 'chunk-uploader'
+ )
);
}
if (!is_dir($directory)) {
throw new NotExistsDirectoryException(
$directory,
sprintf(
- 'Directory %s is not exist',
+ $this->translateContext(
+ 'Directory %s is not exist',
+ 'chunk-uploader'
+ ),
$directory
)
);
@@ -121,7 +150,10 @@ private function assertDirectory(string $directory): void
throw new DirectoryUnWritAbleException(
$directory,
sprintf(
- 'Directory %s is not writable',
+ $this->translateContext(
+ 'Directory %s is not writable',
+ 'chunk-uploader'
+ ),
$directory
)
);
@@ -153,9 +185,42 @@ public function getLimitMaxFileSize(): ?int
return $this->limitMaxFileSize;
}
- public function setLimitMaxFileSize(?int $limitMaxFileSize): void
+ public function setLimitMaxFileSize(?int $limitMaxFileSize): ?int
{
+ if (is_int($limitMaxFileSize)) {
+ // minimum 500KiB
+ $limitMaxFileSize = max($limitMaxFileSize, self::DEFAULT_MIN_FILE_SIZE);
+ if (is_int($this->limitMinimumFileSize)
+ && $limitMaxFileSize < $this->limitMinimumFileSize
+ ) {
+ // assert min
+ $this->limitMinimumFileSize = min($limitMaxFileSize, $this->limitMinimumFileSize);
+ }
+ }
$this->limitMaxFileSize = $limitMaxFileSize;
+ return $this->limitMaxFileSize;
+ }
+
+ public function getMaxUploadFileSize(): int
+ {
+ return $this->maxUploadFileSize;
+ }
+
+ public function setLimitMinimumFileSize(?int $limitMinFileSize): ?int
+ {
+ if (is_int($limitMinFileSize)) {
+ $limitMinFileSize = min($limitMinFileSize, $this->getMaxUploadFileSize());
+ $limitMinFileSize = min($limitMinFileSize, $this->getLimitMaxFileSize());
+ // minimum is 1024
+ $limitMinFileSize = max($limitMinFileSize, self::MIN_FILE_SIZE);
+ }
+ $this->limitMinimumFileSize = $limitMinFileSize;
+ return $this->limitMinimumFileSize;
+ }
+
+ public function getLimitMinimumFileSize(): ?int
+ {
+ return $this->limitMinimumFileSize;
}
public function appendResponseBytes(ResponseInterface $response): ResponseInterface
@@ -176,8 +241,8 @@ public function createProcessor(
public function clean(?int $max = null) : int
{
- $max ??= $this->maxDeletionCount;
- if (!is_dir($this->uploadCacheStorageDirectory) || $max <= 0) {
+ $max ??= $this->getMaxDeletionCount();
+ if ($max <= 0 || !is_dir($this->uploadCacheStorageDirectory)) {
return 0;
}
@@ -185,9 +250,14 @@ public function clean(?int $max = null) : int
foreach (new DirectoryIterator(
$this->uploadCacheStorageDirectory
) as $item) {
+ if ($max <= 0) {
+ break;
+ }
+ $isPartial = $item->getExtension() !== $this->partialExtension;
+ $isPartialMeta = $item->getExtension() !== $this->partialMetaExtension;
if ($item->isDot()
- || $item->getExtension() !== $this->partialExtension
|| $item->getBasename()[0] === '.'
+ || (!$isPartial && !$isPartialMeta)
|| !$item->isFile()
|| $item->isLink()
) {
@@ -199,8 +269,8 @@ public function clean(?int $max = null) : int
if (!$item->isWritable()) {
continue;
}
- if ($max-- < 0) {
- break;
+ if ($isPartial) {
+ $max--;
}
$deleted++;
unlink($item->getRealPath());
diff --git a/src/Uploader/ChunkHandler.php b/src/Uploader/ChunkHandler.php
index 5c22a68..b97dbef 100644
--- a/src/Uploader/ChunkHandler.php
+++ b/src/Uploader/ChunkHandler.php
@@ -16,11 +16,23 @@
use ArrayAccess\TrayDigita\Util\Filter\Consolidation;
use Psr\Http\Message\ResponseInterface;
use SplFileInfo;
+use function file_get_contents;
+use function file_put_contents;
+use function filesize;
+use function is_array;
use function is_file;
+use function is_float;
+use function is_int;
+use function is_string;
+use function json_decode;
+use function json_encode;
+use function microtime;
use function preg_match;
use function preg_quote;
use function sprintf;
use function substr;
+use function unlink;
+use const JSON_UNESCAPED_SLASHES;
class ChunkHandler
{
@@ -41,6 +53,25 @@ class ChunkHandler
*/
public readonly string $targetCacheFile;
+ public readonly string $targetCacheMetaFile;
+
+ /**
+ * @var array{
+ * first_time: ?float,
+ * size: ?int,
+ * count: ?int,
+ * mimetype: ?string,
+ * timing: array
+ * }
+ */
+ private array $metadata = [
+ 'first_time' => null,
+ 'size' => null,
+ 'count' => null,
+ 'mimetype' => null,
+ 'timing' => [],
+ ];
+
/**
* @var int
*/
@@ -81,7 +112,10 @@ public function __construct(
if (!$this->processor->requestIdHeader->valid) {
throw new InvalidRequestId(
sprintf(
- 'Request id "%s" is not valid',
+ $this->processor->chunk->translateContext(
+ 'Request id "%s" is not valid',
+ 'chunk-uploader'
+ ),
$this->processor->requestIdHeader->header
)
);
@@ -93,6 +127,11 @@ public function __construct(
$this->processor->requestIdHeader->header,
$this->processor->chunk->partialExtension
);
+ $this->targetCacheMetaFile = sprintf(
+ '%1$s.%2$s',
+ $this->targetCacheFile,
+ $this->processor->chunk->partialMetaExtension
+ );
}
public function appendResponseHeader(ResponseInterface $response) : ResponseInterface
@@ -122,12 +161,35 @@ public function getLastTarget(): ?string
return $this->lastTarget;
}
+ /**
+ * @return array{
+ * first_time: ?float,
+ * size: ?int,
+ * count: ?int,
+ * mimetype: ?string,
+ * timing: array
+ * }
+ */
+ public function getMetadata(): array
+ {
+ return $this->metadata;
+ }
+
public function deletePartial(): bool
{
+ $status = false;
if (is_file($this->targetCacheFile)) {
- return unlink($this->targetCacheFile);
+ $status = Consolidation::callbackReduceError(
+ fn () => unlink($this->targetCacheFile)
+ );
+ }
+ if (is_file($this->targetCacheMetaFile)) {
+ $new_status = Consolidation::callbackReduceError(
+ fn () => unlink($this->targetCacheMetaFile)
+ );
+ $status = $status || $new_status;
}
- return false;
+ return $status;
}
/**
@@ -158,7 +220,10 @@ protected function check() : int
if (!is_writable($uploadDirectory)) {
$this->status = self::STATUS_NOT_READY;
throw new DirectoryUnWritAbleException(
- 'Cache upload storage is not writable.'
+ $this->processor->chunk->translateContext(
+ 'Cache upload storage is not writable.',
+ 'chunk-uploader'
+ )
);
}
@@ -168,9 +233,11 @@ protected function check() : int
);
$this->status = self::STATUS_READY;
- $this->size = is_file($this->targetCacheFile)
- ? filesize($this->targetCacheFile)
- : 0;
+ if (is_file($this->targetCacheFile)) {
+ $this->size = filesize($this->targetCacheFile);
+ } else {
+ $this->size = 0;
+ }
return $this->status;
}
@@ -205,7 +272,10 @@ private function writeResource(string $mode) : int
$this->status = self::STATUS_FAIL;
throw new FileUnWritAbleException(
$this->targetCacheFile,
- 'Upload cache file is not writable.'
+ $this->processor->chunk->translateContext(
+ 'Upload cache file is not writable.',
+ 'chunk-uploader'
+ )
);
}
@@ -216,7 +286,10 @@ private function writeResource(string $mode) : int
if (!is_resource($this->cacheResource)) {
$this->status = self::STATUS_FAIL;
throw new SourceFileFailException(
- 'Can not create cached stream.'
+ $this->processor->chunk->translateContext(
+ 'Can not create cached stream.',
+ 'chunk-uploader'
+ )
);
}
@@ -224,7 +297,10 @@ private function writeResource(string $mode) : int
if ($wouldBlock) {
throw new FileLockedException(
$this->targetCacheFile,
- 'Cache file has been locked.'
+ $this->processor->chunk->translateContext(
+ 'Cache file has been locked.',
+ 'chunk-uploader'
+ )
);
}
@@ -237,10 +313,58 @@ private function writeResource(string $mode) : int
while (!$uploadedStream->eof()) {
$this->written += (int) fwrite($this->cacheResource, $uploadedStream->read(2048));
}
-
+ $isFirst = $this->size === 0;
$stat = Consolidation::callbackReduceError(fn () => fstat($this->cacheResource));
$this->size = $stat ? (int) ($stat['size']??$this->size+$this->written) : ($this->size+$this->written);
flock($this->cacheResource, LOCK_EX);
+ $written = null;
+ $time = $_SERVER['REQUEST_FLOAT_TIME']??null;
+ $time = is_float($time) ? $time : microtime(true);
+ if (is_file($this->targetCacheMetaFile)) {
+ $meta = Consolidation::callbackReduceError(fn () => json_decode(
+ file_get_contents($this->targetCacheMetaFile),
+ true
+ ));
+ $valid = is_array($meta)
+ && isset($meta['first_time'], $meta['mimetype'], $meta['count'], $meta['timing'])
+ && is_string($meta['mimetype'])
+ && is_float($meta['first_time'])
+ && is_int($meta['count'])
+ && is_array($meta['timing'])
+ && count($meta['timing']) === $meta['count']
+ && preg_match('~^[^/]+/~', $meta['mimetype']);
+ if (!$valid) {
+ Consolidation::callbackReduceError(fn () => unlink($this->targetCacheMetaFile));
+ } else {
+ $written = $meta;
+ $written['count'] += 1;
+ $written['timing'][] = [
+ 'time' => $time,
+ 'written' => $this->written,
+ 'size' => $this->size
+ ];
+ }
+ } elseif ($isFirst) {
+ $written = [
+ 'first_time' => $time,
+ 'mimetype' => $this->processor->uploadedFile->getClientMediaType(),
+ 'count' => 1,
+ 'timing' => [
+ [
+ 'time' => $time,
+ 'written' => $this->written,
+ 'size' => $this->size
+ ]
+ ]
+ ];
+ }
+ if (is_array($written)) {
+ $this->metadata = $written;
+ Consolidation::callbackReduceError(fn () => file_put_contents(
+ $this->targetCacheMetaFile,
+ json_encode($written, JSON_UNESCAPED_SLASHES)
+ ));
+ }
return $this->written;
}
@@ -267,7 +391,10 @@ public function start(?int $position = null): int
throw new InvalidOffsetPositionException(
$position,
$this->size,
- 'Offset upload position is invalid.'
+ $this->processor->chunk->translateContext(
+ 'Offset upload position is invalid.',
+ 'chunk-uploader'
+ )
);
}
@@ -296,7 +423,10 @@ public function put(
if (!is_file($movedFile?:$this->targetCacheFile)) {
throw new SourceFileNotFoundException(
$movedFile?:$this->targetCacheFile,
- 'Source uploaded file does not exist.'
+ $this->processor->chunk->translateContext(
+ 'Source uploaded file does not exist.',
+ 'chunk-uploader'
+ )
);
}
@@ -313,7 +443,10 @@ public function put(
if (!$ready) {
throw new SourceFileMovedException(
sprintf(
- 'Upload cache file is not ready to move : (%d).',
+ $this->processor->chunk->translateContext(
+ 'Upload cache file is not ready to move : (%d).',
+ 'chunk-uploader'
+ ),
$this->status
)
);
@@ -359,7 +492,10 @@ public function put(
throw new FileUnWritAbleException(
$target,
sprintf(
- '%s is not writable.',
+ $this->processor->chunk->translateContext(
+ 'Target file "%s" is not writable.',
+ 'chunk-uploader'
+ ),
$target
)
);
@@ -376,7 +512,10 @@ public function put(
throw new DirectoryUnWritAbleException(
$targetDirectory,
sprintf(
- '%s is not writable.',
+ $this->processor->chunk->translateContext(
+ 'Target directory "%s" is not writable.',
+ 'chunk-uploader'
+ ),
$target
)
);
@@ -392,6 +531,12 @@ public function put(
);
}
+ if (is_file($this->targetCacheMetaFile)) {
+ Consolidation::callbackReduceError(
+ fn() => unlink($this->targetCacheMetaFile)
+ );
+ }
+
$this->lastTarget = null;
if ($result) {
$this->lastTarget = realpath($target) ?: $target;
diff --git a/src/Uploader/ChunkProcessor.php b/src/Uploader/ChunkProcessor.php
index a37a1e8..72ab374 100644
--- a/src/Uploader/ChunkProcessor.php
+++ b/src/Uploader/ChunkProcessor.php
@@ -19,6 +19,7 @@
use function is_int;
use function is_string;
use function sprintf;
+use function var_dump;
final class ChunkProcessor
{
@@ -41,7 +42,10 @@ public function __construct(
$clientFileName = $this->uploadedFile->getClientFilename();
if ($clientFileName === null) {
throw new UnsupportedArgumentException(
- 'Uploaded files does not contain file name.'
+ $this->chunk->translateContext(
+ 'Uploaded files does not contain file name.',
+ 'chunk-uploader'
+ )
);
}
if ($requestIdHeader !== null) {
@@ -129,7 +133,10 @@ public function getHandler() : ChunkHandler
if (!$this->contentRangeHeader->valid) {
throw new InvalidContentRange(
sprintf(
- 'Content range "%s" is invalid',
+ $this->chunk->translateContext(
+ 'Content-Range "%s" is invalid',
+ 'chunk-uploader'
+ ),
$this->contentRangeHeader->header
)
);
@@ -141,7 +148,10 @@ public function getHandler() : ChunkHandler
)) {
throw new InvalidContentRange(
sprintf(
- 'Content range unit "%s" is invalid',
+ $this->chunk->translateContext(
+ 'Content-Range unit "%s" is invalid',
+ 'chunk-uploader'
+ ),
$this->contentRangeHeader->unit
)
);
@@ -149,40 +159,70 @@ public function getHandler() : ChunkHandler
if (!is_int($this->contentRangeHeader->size)) {
throw new ContentRangeIsNotFulFilledException(
- 'System does not support unknown size'
+ $this->chunk->translateContext(
+ 'System does not support unknown size',
+ 'chunk-uploader'
+ )
);
}
}
$size = $this->contentRangeHeader->size;
$limit = $this->chunk->getLimitMaxFileSize();
+ $minimum = $this->chunk->getLimitMinimumFileSize();
$size = !is_int($size) ? null : $size;
$ranges = $this->contentRangeHeader->ranges;
$start = is_array($ranges) ? $ranges[0] : null;
$end = is_array($ranges) ? $ranges[1] : null;
- if ($limit !== null && $limit > 0 && (
+ if ($limit !== null && $limit > 0 && is_int($size) && (
$size > $limit // limit size total
- || $end > $limit // limit position
+ || is_int($end) && $end > $limit // limit position
)) {
throw new OutOfRangeException(
sprintf(
- 'Uploaded file size is bigger than allowed size: %s.',
+ $this->chunk->translateContext(
+ 'Uploaded file size is bigger than allowed size: %s.',
+ 'chunk-uploader'
+ ),
Consolidation::sizeFormat($limit, 4)
)
);
}
+
+ if ($minimum !== null && is_int($size) && $size > $minimum && (
+ $start !== null && $end !== null
+ && $minimum > ($end - $start)
+ && $size > ($start + $end)
+ )) {
+ throw new OutOfRangeException(
+ sprintf(
+ $this->chunk->translateContext(
+ 'Uploaded file size range is less than allowed minimum size: %s.',
+ 'chunk-uploader'
+ ),
+ Consolidation::sizeFormat($minimum, 4)
+ )
+ );
+ }
+
if ($this->isNewRequestId) {
if ($start !== null && $start > 0) {
throw new InvalidOffsetPositionException(
$start,
$this->contentRangeHeader->size,
- 'Content-Range start bytes must be zero without Request-Id.'
+ $this->chunk->translateContext(
+ 'Content-Range start bytes must be zero without Request-Id.',
+ 'chunk-uploader'
+ )
);
}
} elseif (!$this->requestIdHeader->valid) {
throw new InvalidRequestId(
sprintf(
- 'Request id "%s" is not valid',
+ $this->chunk->translateContext(
+ 'Request id "%s" is not valid',
+ 'chunk-uploader'
+ ),
$this->requestIdHeader->header
)
);
@@ -190,7 +230,10 @@ public function getHandler() : ChunkHandler
throw new InvalidOffsetPositionException(
$start,
$size,
- 'Content-Range start bytes must be greater zero with Request-Id.'
+ $this->chunk->translateContext(
+ 'Content-Range start bytes must be greater zero with Request-Id.',
+ 'chunk-uploader'
+ )
);
}
@@ -199,13 +242,19 @@ public function getHandler() : ChunkHandler
$endSize = ($end + 1) - $start;
if ($endSize < $streamSize) {
throw new OutOfRangeException(
- 'Uploaded file size is bigger than ending size.'
+ $this->chunk->translateContext(
+ 'Uploaded file size is bigger than ending size.',
+ 'chunk-uploader'
+ )
);
}
if ($size !== null && $size < $streamSize) {
throw new OutOfRangeException(
- 'Range size is bigger than file size.'
+ $this->chunk->translateContext(
+ 'Range size is bigger than file size.',
+ 'chunk-uploader'
+ )
);
}
diff --git a/src/Util/Filter/ContainerHelper.php b/src/Util/Filter/ContainerHelper.php
index 0e13eb0..cf99c57 100644
--- a/src/Util/Filter/ContainerHelper.php
+++ b/src/Util/Filter/ContainerHelper.php
@@ -104,10 +104,11 @@ public static function decorate(
}
/**
- * @param callable|array|mixed $callable
+ * @template T of object
+ * @param callable|array|class-string|mixed $callable
* @param array $arguments
* @param ContainerInterface|null $container
- * @return array|mixed
+ * @return array|T|mixed
* @throws Throwable
*/
public static function resolveCallable(
diff --git a/src/View/ErrorRenderer/HtmlTraceAbleErrorRenderer.php b/src/View/ErrorRenderer/HtmlTraceAbleErrorRenderer.php
index ff5c95c..de29e6d 100644
--- a/src/View/ErrorRenderer/HtmlTraceAbleErrorRenderer.php
+++ b/src/View/ErrorRenderer/HtmlTraceAbleErrorRenderer.php
@@ -84,6 +84,7 @@ protected function format(
return $view->render(
$path,
[
+ 'displayErrorDetails' => false,
'exception' => $exception
]
);