Search by

moudarir / downloader

moudarir

Modern PHP download library with RFC 9110 support.

Package info

github.com/moudarir/downloader

pkg:composer/moudarir/downloader

Statistics

Installs: 21

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

4.0.2 2026-10-19 18:28 UTC

This package is auto-updated.

Last update: 2026-10-19 18:29:00 UTC


README

Latest Version Tests PHP Version Require License

A lightweight and modern PHP library for streaming files and in-memory data with support for HTTP conditional requests and byte range requests.

Features

  • PHP 8.4+
  • File downloads
  • In-memory data downloads
  • Bandwidth rate limiting
  • HTTP Range requests (RFC 9110)
  • Multipart byte ranges
  • Configurable per-response content length limitation for inline partial responses
  • Mandatory ETag generation
  • Last-Modified support
  • Conditional requests
  • HEAD requests
  • X-Sendfile support
  • X-Accel-Redirect support
  • UTF-8 filenames (RFC 6266)

Requirements

  • PHP 8.4+
  • fileinfo extension for MIME type detection
  • zip extension for ZIP-related MIME type detection
  • (Optional) Apache mod_xsendfile module for ResponseAction::X_SEND_FILE

Installation

composer require moudarir/downloader

Download configuration

Download behavior is configured through DownloadConfig. It centralizes the response action, MIME type, content disposition, ETag strategy, streaming options and server-side delivery settings.

The configuration object can be customized through immutable with...() methods. Each method returns a new configuration instance, so the calls can be chained.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\ContentDisposition;
use Moudarir\Downloader\Enums\ResponseAction;
use Moudarir\File\Enum\MimeType;

$config = new DownloadConfig()
    ->withMimeType(MimeType::MP4)
    ->withResponseAction(ResponseAction::PARTIAL)
    ->withContentDisposition(ContentDisposition::INLINE)
    ->withChunkSize(256 * 1024)
    ->withMaxRangeItems(5)
    ->withLimitRate(0)
    ->withContentLengthLimitation(1024 * 1024);

$response = Download::fromFile(
    '/path/to/video.mp4',
    'video.mp4',
    $config,
);

$response->send();

Configuration options

Method Default Description
withResponseAction() ResponseAction::DEFAULT Selects the response mode.
withMimeType() null Specifies the MIME type. For files, null allows MIME type detection. In-memory data defaults to application/octet-stream when no MIME type is specified.
withContentDisposition() ContentDisposition::ATTACHMENT Controls whether the resource is presented as an attachment or inline.
withETagStrategy() null Selects an ETag generation strategy. When omitted, a supported strategy is selected automatically.
withXAccelRedirectUri() null Sets the internal URI used by Nginx with X-Accel-Redirect.
withChunkSize() DownloadConfig::CHUNK_SIZE (128 KiB) Sets the stream read buffer size.
withMaxRangeItems() DownloadConfig::MAX_RANGE_ITEMS (10) Sets the maximum number of byte ranges accepted in a single request.
withLimitRate() DownloadConfig::BYTES_PER_SECOND (0 = unlimited) Sets the maximum download rate in bytes per second.
withContentLengthLimitation() DownloadConfig::MAX_CONTENT_LENGTH (0 = unlimited) Limits the total body size of an applicable partial response.

Invalid configuration values throw a DownloadException.

Content length limitation

withContentLengthLimitation() limits the size of an actual partial response when both of these settings apply:

  • ResponseAction::PARTIAL
  • ContentDisposition::INLINE

For example, the following configuration limits an applicable HTTP Range response to 1 MiB:

$config = new DownloadConfig()
    ->withResponseAction(ResponseAction::PARTIAL)
    ->withContentDisposition(ContentDisposition::INLINE)
    ->withContentLengthLimitation(1024 * 1024);

The limit applies to each response, not cumulatively across multiple requests. A request without a processed Range does not have its full representation truncated to this limit. A value of 0 disables the limitation.

Usage

Download a file

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\File\Enum\MimeType;

$config = new DownloadConfig()->withMimeType(MimeType::PDF);

$response = Download::fromFile(
    '/path/to/file.pdf',
    config: $config,
);

$response->send();

Download with a custom filename

$config = new DownloadConfig()->withMimeType(MimeType::PDF);

$response = Download::fromFile(
    '/path/to/file.pdf',
    'document.pdf',
    $config,
);

$response->send();

Inline response

The default content disposition is ContentDisposition::ATTACHMENT. To display supported content inline in the browser, configure ContentDisposition::INLINE.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\ContentDisposition;
use Moudarir\File\Enum\MimeType;

$config = new DownloadConfig()
    ->withMimeType(MimeType::PDF)
    ->withContentDisposition(ContentDisposition::INLINE);

$response = Download::fromFile(
    '/path/to/file.pdf',
    config: $config,
);

$response->send();

Partial content and Range requests

For browser-based video playback, configure ResponseAction::PARTIAL together with ContentDisposition::INLINE.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\ContentDisposition;
use Moudarir\Downloader\Enums\ResponseAction;
use Moudarir\File\Enum\MimeType;

$config = new DownloadConfig()
    ->withMimeType(MimeType::MP4)
    ->withResponseAction(ResponseAction::PARTIAL)
    ->withContentDisposition(ContentDisposition::INLINE)
    ->withContentLengthLimitation(1024 * 1024);

$response = Download::fromFile(
    '/path/to/video.mp4',
    'video.mp4',
    $config,
);

$response->send();

ResponseAction::PARTIAL enables HTTP Range request handling, including:

  • Full responses when no valid Range request is processed
  • Single-range responses
  • Multipart byte-range responses
  • Unsatisfiable ranges (416 Range Not Satisfiable)

The optional content length limitation caps applicable partial responses. It does not implement adaptive bitrate streaming: the client or browser still controls which requests it makes.

Download from in-memory data

Use Download::fromData() to send a string as a downloadable resource. Configure its MIME type explicitly when needed.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\File\Enum\MimeType;

$config = new DownloadConfig()->withMimeType(MimeType::OCTET_STREAM);

$response = Download::fromData(
    $data,
    'document.bin',
    $config,
);

$response->send();

When no MIME type is configured for in-memory data, the type defaults to application/octet-stream.

Server-side file delivery

For file resources, delivery can be delegated to the web server using X-Sendfile or X-Accel-Redirect.

Apache — X-Sendfile

The server must have a compatible mod_xsendfile configuration.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\ResponseAction;

$config = new DownloadConfig()->withResponseAction(ResponseAction::X_SEND_FILE);

$response = Download::fromFile(
    '/path/to/file.pdf',
    config: $config,
);

$response->send();

Nginx — X-Accel-Redirect

Configure the internal URI known to Nginx. This URI is not the physical filesystem path.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\ResponseAction;

$config = new DownloadConfig()
    ->withResponseAction(ResponseAction::X_ACCEL_REDIRECT)
    ->withXAccelRedirectUri('/protected/path/to/file.pdf');

$response = Download::fromFile(
    '/path/to/file.pdf',
    config: $config,
);

$response->send();

Server-side response actions are available only for file resources.

Response actions

DownloadConfig::withResponseAction() selects the response mode.

Action Description
ResponseAction::DEFAULT Standard response.
ResponseAction::PARTIAL Enables HTTP Range request handling.
ResponseAction::X_SEND_FILE Delegates file delivery to a compatible web server using X-Sendfile.
ResponseAction::X_ACCEL_REDIRECT Delegates file delivery to Nginx using X-Accel-Redirect.

The returned DownloadResponse must be sent explicitly:

$response->send();

Cache-Control

Use DownloadResponse::addCacheControl() to customize the Cache-Control header for the response.

$response = Download::fromFile('/path/to/file.pdf')->addCacheControl('private, max-age=3600');

$response->send();

The default Cache-Control value is private, must-revalidate.

Response metadata

Download::fromFile() and Download::fromData() return a DownloadResponse instance. Response metadata can be inspected before sending the response.

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\StatusCode;
use Moudarir\File\Enum\MimeType;

$config = new DownloadConfig()->withMimeType(MimeType::PDF);

$response = Download::fromFile(
    '/path/to/file.pdf',
    config: $config,
);

$metadata = $response->metadata();

echo $metadata->statusCode()->value;
echo $metadata->contentLength();
echo $metadata->contentType();
echo $metadata->filename();
echo $metadata->etagValue();

if ($metadata->statusCode() === StatusCode::OK) {
    // ...
}

$response->send();

Available metadata includes:

  • Resource information, including filepath, filename, filesize, MIME type and Last-Modified
  • Response status, Content-Length and Content-Type
  • The selected ResponseAction
  • ETag value and weak/strong state
  • Range information, including single and multipart ranges

statusCode() returns a StatusCode enum.

ETag

ETag generation is mandatory. The library automatically generates an ETag for each supported resource and uses it for HTTP conditional requests.

Available strategies include:

  • ETagStrategy::MTIME
  • ETagStrategy::INODE
  • ETagStrategy::MD5
  • ETagStrategy::SHA256
  • ETagStrategy::SHA512

The selected strategy must be supported by the resource. Otherwise, the library throws a DownloadException.

To specify a strategy:

use Moudarir\Downloader\Download;
use Moudarir\Downloader\DownloadConfig;
use Moudarir\Downloader\Enums\ETagStrategy;

$config = new DownloadConfig()->withETagStrategy(ETagStrategy::SHA256);

$response = Download::fromFile(
    '/path/to/file.pdf',
    config: $config,
);

$response->send();

If no strategy is specified, the library automatically selects a supported strategy.

HTTP features

The library supports:

  • ETag
  • Last-Modified
  • If-Match
  • If-None-Match
  • If-Modified-Since
  • If-Unmodified-Since
  • Range
  • If-Range
  • HEAD

Conditional request evaluation follows the precedence defined by RFC 9110.

If-Range supports both strong ETag comparison and HTTP-date comparison. HTTP-date parsing supports the HTTP-date formats defined by RFC 9110.

HTTP responses

The library supports the following response status codes:

Status code Description
StatusCode::OK 200 OK
StatusCode::PARTIAL_CONTENT 206 Partial Content
StatusCode::NOT_MODIFIED 304 Not Modified
StatusCode::PRECONDITION_FAILED 412 Precondition Failed
StatusCode::RANGE_NOT_SATISFIABLE 416 Range Not Satisfiable

304 Not Modified responses do not contain a message body.

In the current implementation, 412 Precondition Failed and 416 Range Not Satisfiable responses also do not contain a body and use Content-Length: 0. A 416 response includes Content-Range: bytes */<resource-size> when the requested range is unsatisfiable.

License

MIT