Package · sdk-s5w

Folders and files

A folder in S5W is not only a way to group files but a set of rules for them: who sees them, how long they live and how they are cached. Recipes for folders, finding files and changing their place and name.

Folders

Create a folder with rules

php
use Flytachi\Winter\SDK\S5w\Enum\Retention;

$folders = $s5w->folders();

$folders->create('avatars', public: true);                    // open to all, kept forever
$folders->create('tmp-exports', retention: Retention::WEEK);   // private, files live a week
Rule What it means for the files inside
public: true served at publicUrl without a key — for site images, avatars, public downloads
public: false (default) only with a key, through privateUrl
retention deleted by themselves a set time after each file was created — from a day to a year

A folder name is 1 to 100 characters: letters, digits, space, ., _, -; it starts with a letter or a digit. There is no nesting — one level. A taken name gives ConflictException.

Create only if missing

For code that runs many times (application start, a migration), ensure() is handier:

php
$folder = $s5w->folders()->ensure('avatars', public: true);

ensure() looks the folder up first and, if it exists, returns it as it is — the given settings are not applied to an existing folder. If it is missing, it is created with them.

Find a folder

php
$folders->get('avatars');      // Folder or NotFoundException
$folders->find('avatars');     // Folder or null
$folders->exists('avatars');   // bool

A name may contain spaces and non-Latin letters — the SDK encodes it in the address itself.

Change a folder

php
$folders->update('avatars', public: false);                  // only the visibility
$folders->update('tmp-exports', retention: Retention::DAY);  // only the retention
$folders->rename('avatars', 'userpics');                     // only the name

update() changes only what you pass; the rest stays. The server takes the folder as a whole, so when not everything is passed, the SDK reads the current folder first — two requests instead of one.

Changes reach the files

Changing the visibility rewrites the flag on every file in the folder. Changing the retention recomputes each file’s expiry from its creation date: going from YEAR to DAY deletes files older than a day on the next sweep (it runs every five minutes).

Cache

The Cache-Control header the server delivers the folder’s files with is set separately:

php
use Flytachi\Winter\SDK\S5w\Enum\CacheVisibility;

$folders->setCache('avatars', 86400, CacheVisibility::SHARED);   // public, max-age=86400
$folders->setCache('avatars', null, CacheVisibility::PRIVATE);   // private, max-age from the bucket
$folders->resetCache('avatars');                                 // everything from the bucket

null in a parameter means “take the bucket’s”. setCache() always sends both values. How the server picks the header — in the S5W documentation.

List folders

php
$page = $folders->list(page: 1, limit: 25, search: 'user');

foreach ($folders->all() as $folder) {
  echo "{$folder->name} — {$folder->retention->name}\n";
}

Delete a folder

php
$folders->delete('tmp-exports');

Together with its files

A folder is deleted together with every file inside, with no confirmation and no way back. There is no “move the files to the root” mode.

Finding files

A list with filters

php
use Flytachi\Winter\SDK\S5w\Enum\FileSort;
use Flytachi\Winter\SDK\S5w\Enum\SortDirection;

$page = $s5w->files()->list(
  page: 1,
  limit: 25,
  search: 'invoice',             // part of the name, case-insensitive
  folder: 'docs',                // one folder
  sort: FileSort::SIZE,
  direction: SortDirection::DESC,
);

echo "Found {$page->total}, page {$page->current} of {$page->pages}\n";
foreach ($page as $file) {
  echo "{$file->name}  {$file->content->size}\n";
}

The folder: parameter picks where to look:

Value Where it looks
not passed (null) the whole bucket
'docs' or a Folder one folder
Folder::ROOT only the files outside any folder (the root)
php
use Flytachi\Winter\SDK\S5w\Entity\Folder;

$s5w->files()->list(folder: Folder::ROOT);   // the root only

A missing folder in the filter is ValidationException, not an empty list.

Sorting:

FileSort Order
CREATED (default) by upload time
NAME by name
TYPE by MIME type, then name
SIZE by size, then name

The direction is SortDirection::DESC (default, newest and largest first) or ASC.

Sorting by name is case-sensitive

The server sorts names byte by byte: capitals come before lower case — Banana.txt, Report.md, apple.txt.

Walk every file

list() returns one page. To go through all of them there is all() — a generator that fetches the next page itself when the current one runs out:

php
$total = 0;
foreach ($s5w->files()->all(folder: 'backups') as $file) {
  $total += $file->content->size;
}
echo "{$total} bytes in backups\n";

Pages are taken 100 files at a time (the server’s maximum), so most folders are one request. It takes the same filters as list(), except the page number.

Don't change the selection while walking it

The server pages by number. Deleting or moving files inside a foreach over all() shifts the following pages, and some files get skipped. Collect the list first — iterator_to_array($files->all(…), false) — then change.

One file

php
$file = $s5w->files()->get('FXOkqegbdrNRWQgc');   // File or NotFoundException
$file = $s5w->files()->find($id);                  // File or null
$s5w->files()->exists($id);                        // bool

A string that cannot be an id (not 16 characters of A-Za-z0-9_-) is rejected by the SDK itself, with no request: find() returns null. Handy when the id comes from a page address. To check the shape up front use File::isValidId($id).

Changing files

Rename

php
$file = $s5w->files()->rename($file, 'Q4 report.pdf');

The file stays in its folder and its id does not change — every link keeps working. The name is stored as given: the server neither checks nor adds an extension. If the folder already has that name — ConflictException.

Move

php
$file = $s5w->files()->move($file, 'archive');        // into a folder by name
$file = $s5w->files()->move($file, $archiveFolder);   // into a Folder
$file = $s5w->files()->move($file, Folder::ROOT);     // into the root

The destination is required: move($file) and move($file, null) are PHP errors before any request. The name stays. After the move the file takes the new folder’s rules:

Where to What changes for the file
a public folder becomes public, gets a publicUrl
a private folder becomes private, publicUrl = null
a folder with a retention the expiry counts from the file’s creation date
the root becomes public

The root makes a file public

Moving a private file into the root opens it to everyone at publicUrl, without a key. That is why the root can only be given explicitly — Folder::ROOT or ''.

Rename and move at once

php
$file = $s5w->files()->update($file, 'Q4-final.pdf', 'archive');

update() sets the name and the folder in one request and reads nothing first. Both values are required.

How it differs from rename() and move():

rename() / move() update()
requests 2 (read + change) 1
what you pass only what changes the name and the folder
if someone changed the file between the read and the write the change may roll back to what was read no: the result is exactly what you passed

Take update() when both values are known or both changes must happen atomically.

Delete

php
$s5w->files()->delete($file);   // or delete($id)

The file disappears from every delivery address at once; temporary links to it start answering 404. Deleting again is NotFoundException: deletion is not idempotent, so on a retry after a broken connection 404 means “already deleted”. If another file has the same content (deduplication), that one is unaffected.

See also