Skip to content

Setting up your file places

A hands-on guide: in about ten minutes your application says, once, where its files live — and from then on every file operation on CkFile knows the folder behind a FilePlace, the extensions it takes and the most bytes a file may have. As shipped nothing is set, on purpose: the library cannot touch a disk until you have said where, and this page is the saying. You type, you run, you see the answer change.

You need the library's autoloader in a script first — two minutes on Getting started if you haven't done it.

Step 1 — a script to play in

Make places.php next to the library's folder, and paste this:

// no-check
<?php
require __DIR__ . '/CommonKit/php/vendor/autoload.php';

use SafeCoder\CommonKit\CkFile;
use SafeCoder\CommonKit\Enums\FilePlace;
use SafeCoder\CommonKit\Exceptions\CkFileException;

try {
    echo CkFile::FullPath(FilePlace::Documents, 'check.pdf', throwIfInvalid: true), "\n";
} catch (CkFileException $refused) {
    echo $refused->Reason, "\n";
}

Run it with php places.php:

no folder is set for Documents

That is the library as shipped. FullPath is the cheapest question you can ask a place — it reads nothing and writes nothing, it only answers where a file would go — and with throwIfInvalid: true it says why it cannot. Keep this script around: it is the check you run after every change below.

Step 2 — on a platform, one line

On Joomla!, Laravel or WordPress there is nothing to configure but the platform's name. Open CkSettings.php in your copy of the library and set the one constant:

// no-check
public const FilePlatform = FilePlatform::Laravel;   // or Joomla, or WordPress

From then on each place is the folder that platform keeps for such files, found through the platform's own constant or function — JPATH_ROOT, storage_path(), wp_upload_dir() — with no path to write anywhere:

Place Joomla! Laravel WordPress
Images images/ storage/app/public/images/ wp-content/uploads/images/
Documents documents/ storage/app/documents/ wp-content/uploads/documents/
Uploads uploads/ storage/app/uploads/ wp-content/uploads/uploads/
Temporary tmp/ storage/app/temporary/ wp-content/uploads/temporary/

The whole table, for all ten places, is on the FilePlatform page. The platform's base — images/, storage/app, the uploads folder — must exist, and it does on any installed site; the place's folder below it is made the first time something is written there. Run the script from inside the application and the answer is a path under that base. Outside the application, where the platform has not loaded, it says so:

the platform's root is not available (storage_path is not defined)

The rest of this page is the other way: no platform, one root of your own. It is also how a single place is moved elsewhere on any platform, in Step 5.

Step 3 — one root of your own

Make a folder for the files, anywhere outside the application: /var/app/files on a server, /Users/you/Files on a Mac, D:\Files on Windows. It has to exist already. Then tell the library where it is. The quickest door is a variable, which a .env file sets in Laravel and putenv() sets in a script; for the tutorial the script makes the folder and names it before the first file operation:

use SafeCoder\CommonKit\CkFile;
use SafeCoder\CommonKit\Enums\FilePlace;

$root = sys_get_temp_dir() . '/commonkit-places';   // the tutorial's folder; yours is wherever you keep files

if (is_dir($root) === false) {
    mkdir($root);
}

putenv('CommonKit__Files__Root=' . $root);           // before the first file operation: the library reads it once

CkFile::FullPath(FilePlace::Documents, 'check.pdf') === $root . '/Documents/check.pdf';   // -> true

Documents is the subfolder named after the place. It does not exist yet, and need not: the first write makes it. Every place works like that, from the one root — Images, Audio, Imports — the ten names are on the FilePlace page. Write something and watch it appear:

use SafeCoder\CommonKit\CkFile;
use SafeCoder\CommonKit\Enums\FilePlace;

$root = sys_get_temp_dir() . '/commonkit-places';

if (is_dir($root) === false) {
    mkdir($root);
}

putenv('CommonKit__Files__Root=' . $root);

CkFile::Write(FilePlace::Imports, 'feed.csv', "id,name\n1,Mary\n", overwrite: true);   // -> true
CkFile::Exists(FilePlace::Imports, 'feed.csv');                                       // -> true
CkFile::Read(FilePlace::Imports, 'feed.csv');                                         // -> "id,name\n1,Mary\n"
CkFile::Files(FilePlace::Imports);                                                    // -> ['feed.csv']

Imports is there now under the root, with feed.csv in it. The write passed because Imports takes a csv and the content is text; what a place checks before it takes a file is on the CkFile page, under Your file places, set once. The same three doors as the root — a variable, the code, and nothing else — set the rules per place:

CommonKit__Files__Root=/var/app/files
CommonKit__Files__Documents__MaxBytes=10485760
CommonKit__Files__Documents__Extensions=pdf,docx,csv
CommonKit__Files__Archives__Root=/mnt/archive
CommonKit__Files__Blocked=rar,7z

Every key stands alone: Documents names a limit and a list and keeps the root; Archives has a folder of its own and keeps the default limit and list; a place not named at all keeps everything. Three small rules for the values: MaxBytes is a number of bytes (10485760, never 10 MB), and zero or anything unreadable means "not given"; an extension is written without its dot, and casing does not matter; and a folder of its own must exist, like the root, since only the subfolders under the root are made for you. Blocked adds to the library's own blocked list and takes nothing from it: nothing you write can make the library take a php or an exe.

Step 4 — in code, when you'd rather not have a variable

The library travels as source, so the other door is CkSettings.php in your copy of it: the constant DefaultFileRoot for the root, and three methods — FileRoot, FileMaxBytes, FileExtensions — each a match over the places, answering null for every one as shipped. Fill in what you want:

// no-check
public const DefaultFileRoot = '/var/app/files';

public static function FileRoot(FilePlace $place): ?string
{
    return match ($place) {
        FilePlace::Archives => '/mnt/archive',
        default => null,
    };
}

public static function FileMaxBytes(FilePlace $place): ?int
{
    return match ($place) {
        FilePlace::Documents => 10_485_760,
        default => null,
    };
}

The constant takes a literal; the methods may answer anything — a literal, getenv('STORAGE_ROOT'), a path built with dirname(). The variables override these key by key, so what you write here is the floor, not a lock: a fixed value is right for a server that never moves, and the variable is right for everything else.

Step 5 — one place elsewhere, on any platform

FileRoot and the per-place variable win over the platform too. A Laravel site whose archives live on a mounted volume keeps FilePlatform::Laravel for everything else and says, in .env:

CommonKit__Files__Archives__Root=/mnt/archive

The folder must exist, as any folder of a place's own must, and the rest of the places stay where the platform keeps them.

When a place still says no

Every refusal names the place and says why. These are the ones a setting causes, as FullPath reports them with throwIfInvalid: true:

The reason What to look at
no folder is set for Documents No platform is named and no root reached the library. Is the variable set before the first file operation? Is it spelled CommonKit__Files__Root?
the platform's root is not available (JPATH_ROOT is not defined) The platform has not loaded: the call runs outside the application, or before the platform's bootstrap.
the folder set for Documents is not an absolute path files or ../files: write the whole path.
the folder set for Documents is a root folder / or D:\: a place is a folder of its own, never a whole drive.
the folder set for Documents is the application's own folder, or one above it The root holds the application — the document root, the script's folder — so ../configuration.php would be in bounds. Move the files out.
the folder set for Documents does not exist The root, or the place's own folder, is not there yet. Make it; the library makes only the folders below.

Two facts behind the table. The variables are read once, at the first file operation of the request: a value set after it is never seen, which is why the tutorial's putenv() comes first. And the application's own folder is the web server's document root when there is a request, the running script's folder otherwise: a root set to the site itself is refused on purpose.

Where next

  • Taking an upload — the handler, now that the places are set: the name cleaned, the file judged, a CSV read as data.
  • FilePlace — the ten places and what each takes by default; FilePlatform — where each platform keeps them.
  • Your file places, set once — the reference: every key, the defaults, and the rules that stand whatever you set.
  • How it works — the five rules, and which reader to call.