upload-interop/interface

1.0.0-alpha2 2025-06-28 15:14 UTC

This package is not auto-updated.

Last update: 2025-07-02 20:18:48 UTC


README

PDS Skeleton PDS Composer Script Names

Upload-Interop provides an interoperable package of standard interfaces for working with upload structures in PHP 8.4+. It reflects, refines, and reconciles the common practices identified within several pre-existing projects.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174).

This package attempts to adhere to the Package Development Standards approach to naming and versioning.

Interfaces

Upload-Interop defines these interfaces:

Upload-Interop also defines an UploadTypeAliases interface with PHPStan types to aid static analysis.

UploadStruct

The UploadStruct interface represents the $_FILES values for a single uploaded file. It defines these properties:

  • string $tmp_name { get; }

    • Corresponds to the 'tmp_name' key in a files_item_array.
  • int $error { get; }

    • Corresponds to the 'error' key in a files_item_array.
  • ?string $name { get; }

    • Corresponds to the 'name' key in a files_item_array.
  • ?string $full_path { get; }

    • Corresponds to the 'full_path' key in a files_item_array.
  • ?string $type { get; }

    • Corresponds to the 'type' key in a files_item_array.
  • ?int $size { get; }

    • Corresponds to the 'size' key in a files_item_array.

Notes:

  • The interfaces defines property hooks for get but not set. The interface only guarantees readability; writability is outside the scope of this package.

  • The properties are in snake_case, not camelCase. This maintains a direct 1:1 correspondence between the native $_FILES array keys and the UploadStruct properties.

  • There are no affordances for operating on the uploaded file itself. Reading from, writing to, moving, copying, renaming, etc. an uploaded file are application-specific concerns, independent from any specific UploadStruct implementation.

UploadStructFactory

The UploadStructFactory interface affords creating a single UploadStruct instance ...

  • public function newUpload(
        string $tmp_name,
        int $error,
        ?string $name = null,
        ?string $full_path = null,
        ?string $type = null,
        ?int $size = null,
    ) : UploadStruct;

... or an uploads_array of UploadStruct instances parsed from $_FILES (or its equivalent):

  • public function newUploadsFromFiles(files_array $files) : uploads_array;

The uploads_array index structure returned by newUploadsFromFiles() MUST correspond to the structure in which the files_array fields were indexed; cf. README-FILES.md.

UploadThrowable

The UploadThrowable interface marks an Exception as upload-related. It adds no class members.

UploadTypeAliases

The UploadTypeAliases interface provides these custom PHPStan types to aid static analysis:

  • files_array: array<array-key, files_item_array|files_group_array|files_array> recursively up to 16 dimensions.

  • files_group_array:

    array{
        tmp_name:string[],
        error:int[],
        name?:string[],
        full_path?:string[],
        type?:string[],
        size?:int[],
    }
    
  • files_item_array:

    array{
        tmp_name:string,
        error:int,
        name?:string,
        full_path?:string,
        type?:string,
        size?:int,
    }
    
  • uploads_array: array<array-key, UploadStruct|uploads_array> recursively up to 16 dimensions.

Notes:

  • The files_* types are defined from the $_FILES structure. Cf. https://www.php.net/manual/en/features.file-upload.post-method.php.

  • The *_[00-0F] types are to enable limited recursion. PHPStan does not handle recursive type aliases, so files_array and uploads_array cannot ever refer back to themselves. As a result, those type aliases refer to the *_[00-0F] types to enable recursion to 16 dimensions. Consumers need not use these recursion-enabling type aliases.

Implementations

Implementations MAY validate UploadStruct values; implementations doing so MUST throw an UploadThrowable when a value is invalid.

Implementations advertised as readonly or immutable MUST be deeply readonly or immutable; they MUST NOT encapsulate any references, resources, mutable objects, objects or arrays encapsulating references or resources or mutable objects, and so on.

Implementations MAY define additional class members not defined in these interfaces; implementations advertised as readonly or immutable MUST make those additional class members deeply readonly or immutable.

Notes:

  • Reflection does not invalidate advertisements of readonly or immutable implementations. The ability of a consumer to use Reflection to mutate an implementation advertised as readonly or immutable does not constitute a failure to comply with Upload-Interop.

  • Reference implementations are available at https://github.com/upload-interop/impl.

Q & A

Why a separate Upload-Interop?

Whereas the key structures of $_GET, $_POST, etc. superglobal arrays are not well-defined, the terminating files_array_item data structure in the $_FILES superglobal is well-defined. However, one wants to be able to pass that data structure (or a representation of it) into presentation-independent application or domain logic. As such, one would prefer something that is not tied to a particular presentation format.

For example, embedding the Upload-Interop structures in an HTTP-related standard could reasonably be considered to be tying the structures to the HTTP presentation format. That in turn would make Upload-Interop academically unsuitable for application or domain use.

Thus, Upload-Interop being separated from a particular presentation format gives philosophical cover to using UploadStruct instances and the various UploadTypeAliases in application or domain logic, much the same way there is cover for using DateTime or SimpleXmlElement instances in application or domain logic.

Why is there no UploadCollection ?

$_GET and $_POST user inputs are arbitrarily structured from interaction to interaction. Except for the terminating files_array_item, the $_FILES user inputs are likewise arbitrarily structured. An uploads_array is a representation of that arbitrary structure.

As with other user inputs, it is an application-specific concern to map those arbitrary structures to more well-defined ones, such as domain-specific collections.

Why is it an UploadStruct and not just an Upload ?

Upload-Interop wants to avoid Interface suffixes, and wants to avoid making implementors use import aliases. Calling it an Upload would mean any implementation also called Upload would have to alias the interop interface. It is the difference between this less-prefereable alternative ...

use UploadInterop\Interface\Upload as UploadInteropInterface;

class Upload implements UploadInteropInterface
{
    // ...
}

... and this more-preferable one:

use UploadInterop\Interface\UploadStruct;

class Upload implements UploadStruct
{
    // ...
}

Further, the Upload definition is struct-like in that it is composed only of properties.

It is true that none of the researched implementations use Struct in their naming; but then, the interop is for the interface, so existing implementation names can remain as they are.