dadajuice/zephyrus

View on GitHub
src/Zephyrus/Utilities/HttpUploadReceiver.php

Summary

Maintainability
A
1 hr
Test Coverage
F
0%
<?php namespace Zephyrus\Utilities;

use Zephyrus\Exceptions\HttpUploadException;
use Zephyrus\Network\Request;

class HttpUploadReceiver
{
    private Request $request;
    private string $uuid;
    private int $chunk;
    private int $totalChunks;
    private int $totalFileSize;
    private array $fileData;
    private int $chunkSize;
    private string $path;
    private string $originalFilename;

    public function __construct(Request $request, ?string $uploadDirectory = null)
    {
        $this->request = $request;
        $this->initializeChunkData();
        $this->initializeUploadData();
        $this->initializePath($uploadDirectory);
    }

    /**
     * @throws HttpUploadException
     */
    public function receiveChunk(callable $completedCallback): bool
    {
        if ($this->chunk > $this->totalChunks) {
            throw HttpUploadException::sizeOverflow();
        }
        $this->appendChunk();
        if ($this->chunk == $this->totalChunks) {
            $completedCallback($this->path, $this->originalFilename);
            return true;
        }
        return false;
    }

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

    public function getUuid(): string
    {
        return $this->uuid;
    }

    public function getChunkSize(): int
    {
        return $this->chunkSize;
    }

    public function getCompleteSize(): int
    {
        return $this->totalFileSize;
    }

    public function getChunkNumber(): int
    {
        return $this->chunk;
    }

    public function getChunkTotalCount(): int
    {
        return $this->totalChunks;
    }

    private function appendChunk()
    {
        $handler = fopen($this->path, 'a');
        fwrite($handler, $this->getChunkData());
        fclose($handler);
    }

    private function getChunkData(): string
    {
        $chunkFile = fopen($this->fileData['tmp_name'], "rb");
        return fread($chunkFile, $this->chunkSize);
    }

    private function initializePath(?string $uploadDirectory)
    {
        $uploadDirectory = $this->prepareUploadDirectory($uploadDirectory);
        $path = $uploadDirectory . DIRECTORY_SEPARATOR . $this->buildFilename();
        if ($this->chunk == 1 && file_exists($path)) {
            throw HttpUploadException::alreadyStarted();
        }
        if ($this->chunk > 1 && !file_exists($path)) {
            throw HttpUploadException::noLongerAvailable();
        }
        $this->path = $path;
    }

    private function buildFilename(): string
    {
        $fname = $this->fileData['name'];
        $extension = pathinfo($fname, PATHINFO_EXTENSION);
        $fname = pathinfo($fname, PATHINFO_FILENAME);
        $digest = md5($fname . $this->uuid);
        return $digest . (!empty($extension) ? '.' . $extension : '');
    }

    private function initializeChunkData()
    {
        $uuid = $this->request->getParameter('upload_uuid');
        $chunk = $this->request->getParameter('upload_chunk');
        $totalChunks = $this->request->getParameter('upload_total_chunks');
        $totalFileSize = $this->request->getParameter('upload_total_size');
        if (empty($uuid) || empty($chunk) || empty($totalChunks) || empty($totalFileSize)
            || !is_numeric($chunk) || !is_numeric($totalChunks) || !is_numeric($totalFileSize)) {
            throw HttpUploadException::invalidChunk();
        }
        $this->uuid = $uuid;
        $this->chunk = $chunk;
        $this->totalChunks = $totalChunks;
        $this->totalFileSize = $totalFileSize;
    }

    private function initializeUploadData()
    {
        $fileData = $this->request->getFile('upload_file');
        if (empty($fileData)) {
            throw HttpUploadException::noChunkUploaded();
        }
        if (!Validation::isUpload($fileData)) {
            throw HttpUploadException::invalidUpload();
        }
        $size = filesize($fileData['tmp_name']);
        if ($size <= 0) {
            throw HttpUploadException::emptyChunkUploaded();
        }
        $this->fileData = $fileData;
        $this->chunkSize = $size;
        $this->originalFilename = $fileData['name'];
    }

    private function prepareUploadDirectory(?string $uploadDirectory): string
    {
        if (is_null($uploadDirectory)) {
            $uploadDirectory = sys_get_temp_dir();
        }
        $uploadDirectory = rtrim($uploadDirectory, DIRECTORY_SEPARATOR);
        if (!is_writable($uploadDirectory)) {
            throw HttpUploadException::directoryNotWritable();
        }
        return $uploadDirectory;
    }
}