PHPackages                             timefrontiers/php-file - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. timefrontiers/php-file

ActiveLibrary[File &amp; Storage](/categories/file-storage)

timefrontiers/php-file
======================

TimeFrontiers File — multi-driver file storage, expiring download tokens, and image size constraints

v1.0.5(3w ago)0251MITPHPPHP &gt;=8.3

Since Apr 25Pushed 3w agoCompare

[ Source](https://github.com/timefrontiers/php-file)[ Packagist](https://packagist.org/packages/timefrontiers/php-file)[ Docs](https://github.com/timefrontiers/php-file)[ RSS](/packages/timefrontiers-php-file/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (6)Dependencies (7)Versions (7)Used By (1)

timefrontiers/php-file
======================

[](#timefrontiersphp-file)

Multi-driver file storage for PHP 8.3+. Handles upload, retrieval, and streaming of files across local and cloud storage backends, with expiring/download-limited tokens, image size enforcement, and file ownership.

---

Requirements
------------

[](#requirements)

- PHP 8.3+
- MariaDB 10.4+ / MySQL 8.0+
- `timefrontiers/php-sql-database`
- `timefrontiers/php-database-object`
- `timefrontiers/php-has-errors`
- `timefrontiers/php-pagination`
- `timefrontiers/php-data`
- `timefrontiers/php-validator`
- `gumlet/php-image-resize` *(bundled — used for max-dimension enforcement on upload)*

**Optional:**

- `aws/aws-sdk-php ^3.0` — required when using the S3 or MinIO driver

---

Installation
------------

[](#installation)

```
composer require timefrontiers/php-file
```

For S3 or MinIO support:

```
composer require aws/aws-sdk-php
```

---

Database
--------

[](#database)

Run `sql/install.sql` against your `file` database to create the required tables:

```
file_meta      — primary file record
file_tokens    — expiring / download-limited tokens
file_default   — per-owner default file mappings (e.g. avatar, banner)
folders        — named file containers
folder_files   — folder ↔ file pivot

```

**Migrating from `linktude/php-file`?** See [`sql/migrate.sql`](sql/migrate.sql).

---

Configuration
-------------

[](#configuration)

Call `File::configure()` once in your application bootstrap, before any `File` instance is created.

`base[]` holds global settings. `drivers[]` is a keyed map where each key is a driver name and the value is that driver's config. Only the drivers you declare are available at runtime.

```
use TimeFrontiers\File\File;

File::configure(
    base: [
        'default_driver' => 'local',                     // which driver to use when none is specified
        'path_prefix'    => 'User-Files',                // logical storage namespace — package appends /{owner}
        'db_name'        => 'file',                      // database name
        'service_url'    => 'https://files.example.com', // base URL for your token download endpoint
        'token_secret'   => 'your-hmac-secret',          // used to sign download tokens
        'max_size'       => 1024 * 1024 * 25,            // 25 MB upload limit
        'min_size'       => 0,                           // 0 = no minimum
        'max_width_px'   => 2000,                        // images wider than this are resized down
        'max_height_px'  => 2000,
        'min_width_px'   => null,                        // null = no minimum dimension check
        'min_height_px'  => null,
    ],
    drivers: [
        'local' => [
            'upload_path' => '/var/www/storage',          // absolute root on disk
            'storage_url' => 'https://cdn.example.com',   // base URL for direct public file access
        ],
        's3' => [
            'bucket'      => 'my-bucket',
            'region'      => 'us-east-1',
            'key'         => 'ACCESS_KEY_ID',
            'secret'      => 'SECRET_ACCESS_KEY',
            'endpoint'    => null,                        // set for S3-compatible stores (not MinIO — use 'minio' driver)
            'storage_url' => '',                          // optional CDN override; if empty → native S3 URL
        ],
        'minio' => [
            'endpoint'    => 'http://localhost:9000',     // MinIO server URL (required)
            'bucket'      => 'my-bucket',                 // bucket name (required)
            'region'      => 'us-east-1',                 // ignored by MinIO but required by the SDK
            'key'         => 'minioadmin',
            'secret'      => 'minioadmin',
            'storage_url' => '',                          // optional CDN/public URL override
        ],
    ]
);
```

> **Validation at configure time:** `default_driver` must match a key in `drivers[]`. An unknown driver, a missing `default_driver`, or a non-array driver entry all throw a `ConfigurationException` immediately.

### `path_prefix` — logical storage namespace

[](#path_prefix--logical-storage-namespace)

`base.path_prefix` is appended before `/{owner}` by the package automatically, producing a consistent storage path across **all** drivers:

DriverPhysical / key location`local``{upload_path}/{path_prefix}/{owner}/{filename}``s3`Object key: `{path_prefix}/{owner}/{filename}``minio`Object key: `{path_prefix}/{owner}/{filename}`Set it dynamically at configure-time based on the user's access level:

```
$path_prefix = get_constant('FILE_ACCESS_SCOPE') === 'USER'
    ? 'User-Files'
    : $session->access_group()->value;  // e.g. 'ADMIN'

File::configure(base: ['path_prefix' => $path_prefix, ...], drivers: [...]);
```

### Local driver options

[](#local-driver-options)

KeyRequiredDescription`upload_path`✅Absolute filesystem root where files are stored`storage_url`✅Base URL for direct public access to files### S3 driver options

[](#s3-driver-options)

KeyRequiredDescription`bucket`✅S3 bucket name`region`—AWS region (default `us-east-1`)`key`✅AWS access key ID`secret`✅AWS secret access key`endpoint`—Custom endpoint for S3-compatible stores`storage_url`—CDN override; if empty, native S3 URL is used### MinIO driver options

[](#minio-driver-options)

KeyRequiredDescription`endpoint`✅MinIO server URL (e.g. `http://localhost:9000`)`bucket`✅Bucket name`region`—Ignored by MinIO but required by the SDK (default `us-east-1`)`key`✅MinIO access key`secret`✅MinIO secret key`storage_url`—CDN/public URL override; if empty, URL is built as `{endpoint}/{bucket}/{path}`---

Usage
-----

[](#usage)

### Upload

[](#upload)

```
use TimeFrontiers\File\File;

// Uses base.default_driver — _path is auto-built as /{path_prefix}/{owner}
$file = new File($conn);

// Or select a driver explicitly for this instance
$file = new File($conn, 's3');
$file = new File($conn, 'minio');

$file->privacy('public');    // 'public' | 'private'
$file->owner   = $userCode;
$file->caption = 'Profile photo';

// _path is automatically /{path_prefix}/{userCode} — no setPath() needed
$ok = $file->upload($_FILES['avatar'], owner: $userCode, creator: $userCode);

// Override path explicitly when needed (e.g. system files, shared folders):
// $file->setPath('/System/Shared');

if (!$ok) {
    // $file->getErrors() — HasErrors compatible
}

echo $file->id;           // BIGINT surrogate key
echo $file->code;         // '583...' 15-char human identifier
echo $file->sizeAsText(); // '1.2 MB'
```

### Load an existing file

[](#load-an-existing-file)

```
// by BIGINT id
$file = (new File($conn))->load(42);

// by 15-char code
$file = (new File($conn))->load('583258614696648');

// via QueryBuilder
$files = File::query()
    ->where('owner', $userCode)
    ->where('type_group', 'image')
    ->orderByDesc('_created')
    ->limit(20)
    ->get();
```

### Direct URL (public files only)

[](#direct-url-public-files-only)

```
// URL exposes only the unique filename — internal folder structure is never revealed
echo $file->url();   // https://cdn.example.com/abc123def456789.jpg
```

### Download tokens

[](#download-tokens)

Tokens are HMAC-signed, driver-agnostic, and enforce both time expiry and download limits.

```
// Mint a token — expires in 24 hours, max 5 downloads
$token = $file->createToken(
    expiresAt:    '+24 hours',
    maxDownloads: 5,
    createdBy:    $userCode
);

// Build the full URL to hand to a client
$url = $file->tokenUrl($token);
// → https://files.example.com/download/
```

**On your download endpoint:**

```
// Resolve + validate the token (verifies HMAC, checks expiry and counter)
$file = File::resolveToken($token);

if (!$file) {
    http_response_code(403);
    exit();
}

$file->download($token);       // stream inline  (increments counter)
$file->forceDownload($token);  // force attachment download
```

### Update metadata

[](#update-metadata)

```
$file->caption   = 'Updated caption';
$file->nice_name = 'new-name.jpg';
$file->update();
```

### Lock a file

[](#lock-a-file)

Locking computes a SHA-512 checksum and prevents further updates.

```
$file->lock();

echo $file->locked();    // true
echo $file->checksum();  // sha-512 hex string
```

### Delete a file

[](#delete-a-file)

```
// Refuses if the file is set as a default anywhere
$file->destroy();

// Force removal even if set as a default
$file->destroy(force: true);
```

---

Default files
-------------

[](#default-files)

Map an owner string to one or more named file slots.

```
use TimeFrontiers\File\FileDefault;

// Single default — replaces any existing default for 'avatar'
FileDefault::set($conn, user: $userCode, setKey: 'avatar', fileId: $file->id);

// Multi default — appends to a gallery list
FileDefault::set($conn, user: $userCode, setKey: 'gallery', fileId: $file->id, multiSet: true);

// Retrieve
$default = FileDefault::get($conn, user: $userCode, setKey: 'avatar');  // FileDefault|false
$gallery = FileDefault::getAll($conn, user: $userCode, setKey: 'gallery'); // FileDefault[]

// Remove one entry
FileDefault::remove($conn, user: $userCode, setKey: 'gallery', fileId: $file->id);

// Clear all defaults for a slot
FileDefault::clearSet($conn, user: $userCode, setKey: 'gallery');
```

---

Folders
-------

[](#folders)

```
use TimeFrontiers\File\Folder;
use TimeFrontiers\File\FolderFile;

// Create
$folder          = new Folder($conn);
$folder->name    = 'profile-assets';
$folder->title   = 'Profile Assets';
$folder->owner   = $userCode;
$folder->_author = $userCode;
$folder->create();

// List all folders for an owner
$folders = Folder::forOwner($conn, $userCode);

// Add / remove files
FolderFile::add($conn, folderId: $folder->id, fileId: $file->id);
FolderFile::remove($conn, folderId: $folder->id, fileId: $file->id);

// Files in a folder
$pivots = FolderFile::forFolder($conn, $folder->id);

// Delete folder (removes all pivot rows automatically)
$folder->destroy();
```

---

Storage drivers
---------------

[](#storage-drivers)

DriverStatusNotes`local`✅ FullDefault. Files stored under `drivers.local.upload_path`.`s3`✅ FullRequires `aws/aws-sdk-php`. Standard AWS S3 or S3-compatible endpoint.`minio`✅ FullRequires `aws/aws-sdk-php`. Path-style endpoint enforced. `endpoint` is required.`gcs`🔲 StubThrows `DriverException` — implementation coming.`onedrive`🔲 StubThrows `DriverException` — implementation coming.`dropbox`🔲 StubThrows `DriverException` — implementation coming.The driver used to store a file is recorded in `file_meta.storage_driver`. When a file is loaded from the database, the correct driver is automatically resolved for streaming and deletion — regardless of the current `default_driver` setting.

---

Image constraints
-----------------

[](#image-constraints)

Configured in `base[]`. Applied automatically on every upload before the file reaches storage.

KeyEffect`max_width_px`Images wider than this are resized down (aspect ratio preserved)`max_height_px`Images taller than this are resized down`min_width_px`Images narrower than this are **rejected**`min_height_px`Images shorter than this are **rejected**Set any value to `null` to disable that constraint. Non-image files pass through all checks untouched.

> **Note:** Full image manipulation (crop, rotate, watermark) is planned for `timefrontiers/php-image`.

---

Ownership
---------

[](#ownership)

All ownership fields are `VARCHAR(64)` strings — not foreign keys — so they can hold user codes, system constants, or any arbitrary identifier.

TableColumnExample values`file_meta``owner``'08744307265'`, `'SYSTEM'`, `'SYSTEM.HIDDEN'``file_default``user``'08744307265'`, `'SYSTEM'``folders``owner``'08744307265'`, `'SYSTEM'`---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance95

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

6

Last Release

23d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/dfcd9a5645cb2deb0771b9102be193d63b495564b1e06c0b85cb31bf028d0887?d=identicon)[ikechukwuokalia](/maintainers/ikechukwuokalia)

---

Top Contributors

[![ikechukwuokalia](https://avatars.githubusercontent.com/u/4541227?v=4)](https://github.com/ikechukwuokalia "ikechukwuokalia (7 commits)")

---

Tags

phps3filestorageuploaddownloadminiotimefrontiers

### Embed Badge

![Health badge](/badges/timefrontiers-php-file/health.svg)

```
[![Health](https://phpackages.com/badges/timefrontiers-php-file/health.svg)](https://phpackages.com/packages/timefrontiers-php-file)
```

###  Alternatives

[league/flysystem

File storage abstraction for PHP

13.6k665.7M2.4k](/packages/league-flysystem)[league/flysystem-aws-s3-v3

AWS S3 filesystem adapter for Flysystem.

1.7k277.8M952](/packages/league-flysystem-aws-s3-v3)[mwguerra/filemanager

A full-featured file manager package for Laravel and Filament v5 with dual operating modes, drag-and-drop uploads, S3/MinIO support, and comprehensive security features.

7715.5k2](/packages/mwguerra-filemanager)[blueimp/jquery-file-upload

File Upload widget for jQuery.

141.5M19](/packages/blueimp-jquery-file-upload)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
