phplrt/phplrt

View on GitHub
libs/source/src/SourceFactory.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

declare(strict_types=1);

namespace Phplrt\Source;

use Phplrt\Contracts\Source\SourceFactoryInterface;
use Phplrt\Contracts\Source\FileInterface;
use Phplrt\Contracts\Source\ReadableInterface;
use Phplrt\Source\Exception\NotCreatableException;
use Phplrt\Source\Exception\NotFoundException;
use Phplrt\Source\Exception\NotReadableException;

final class SourceFactory implements SourceFactoryInterface
{
    /**
     * Default chunk size value.
     *
     * @var int<1, max>
     */
    public const DEFAULT_CHUNK_SIZE = 65536;

    /**
     * Default hashing algorithm value.
     *
     * @var non-empty-string
     */
    public const DEFAULT_HASH_ALGO = 'md5';

    /**
     * Default name of the temporary streams.
     *
     * @var non-empty-string
     */
    public const DEFAULT_TEMP_STREAM = 'php://memory';

    /**
     * @var non-empty-string
     * @psalm-readonly-allow-private-mutation
     */
    private string $algo = self::DEFAULT_HASH_ALGO;

    /**
     * @var non-empty-string
     * @psalm-readonly-allow-private-mutation
     */
    private string $temp = self::DEFAULT_TEMP_STREAM;

    /**
     * @var int<1, max>
     * @psalm-readonly-allow-private-mutation
     */
    private int $chunkSize = self::DEFAULT_CHUNK_SIZE;

    /**
     * @param non-empty-string $algo Hashing algorithm for the sources.
     * @param non-empty-string $temp The name of the temporary stream, which is
     *        used as a resource during the reading of the source.
     * @param int<1, max> $chunkSize The chunk size used while non-blocking
     *        reading the file inside the {@see \Fiber} context.
     */
    public function __construct(
        string $algo = self::DEFAULT_HASH_ALGO,
        string $temp = self::DEFAULT_TEMP_STREAM,
        int $chunkSize = self::DEFAULT_CHUNK_SIZE
    ) {
        assert($algo !== '', 'Hashing algorithm name must not be empty');
        assert($temp !== '', 'Temporary stream name must not be empty');
        assert($chunkSize >= 1, 'Chunk size must be greater than 0');

        $this->chunkSize = $chunkSize;
        $this->temp = $temp;
        $this->algo = $algo;
    }

    public function create($source): ReadableInterface
    {
        return match (true) {
            $source instanceof ReadableInterface => $source,
            $source instanceof \SplFileInfo => $this->createFromFile($source->getPathname()),
            \is_string($source) => $this->createFromString($source),
            \is_resource($source) => $this->createFromStream($source),
            default => throw NotCreatableException::fromInvalidType($source),
        };
    }

    public function createFromString(string $content = '', string $name = null): ReadableInterface
    {
        assert($name !== '', 'Name must not be empty');

        if ($name === null) {
            return new Source($content, $this->algo, $this->temp);
        }

        return new VirtualFile($name, $content, $this->algo, $this->temp);
    }

    public function createFromFile(string $filename): FileInterface
    {
        if (!\is_file($filename)) {
            throw NotFoundException::fromInvalidPathname($filename);
        }

        if (!\is_readable($filename)) {
            throw NotReadableException::fromOpeningFile($filename);
        }

        return new File($filename, $this->algo, $this->chunkSize);
    }

    /**
     * @throws NotReadableException
     */
    public function createFromStream($stream, string $name = null): ReadableInterface
    {
        assert($name !== '', 'Name must not be empty');

        if (!\is_resource($stream)) {
            throw NotReadableException::fromInvalidResource($stream);
        }

        if (\get_resource_type($stream) !== 'stream') {
            throw NotReadableException::fromInvalidStream($stream);
        }

        if ($name === null) {
            return new Stream($stream, $this->algo, $this->chunkSize);
        }

        return new VirtualStreamingFile($name, $stream, $this->algo, $this->chunkSize);
    }
}