Download the PHP package gravitypdf/upload without Composer

On this page you can find all versions of the php package gravitypdf/upload. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package upload

Upload

codecov License: MIT

A PHP library to validate and save uploaded files.

Why was this library forked?

Installation

Requirements

PHP 7.3 to 8.5 and ext-fileinfo. Optional but recommended: ext-mbstring (or symfony/polyfill-mbstring), which is what guarantees a sanitized filename is valid UTF-8 and lets truncation land on a character boundary. The same repair runs over the strings getErrors() returns, so without it neither is guaranteed and json_encode() can refuse a name that arrived as invalid UTF-8.

Migrating from codeguy/upload? Version 3.x of this package is a drop-in replacement: update your imports from \Upload\… to \GravityPdf\Upload\….

Upgrading from 3.x? Version 4.0 turns new protections on by default. The upgrade guide covers what changed and what to check.

Usage

Single-file upload

Assume a file is uploaded with this HTML form:

Server-side, validate the upload, rename it, and store it:

FileType accepts any registered IANA media type, such as image/png or application/pdf.

Reading file metadata

These calls are forwarded to every file in the collection. With one file you get the value back, with several you get an array of values, and with none you get null. When a field accepts multiple files, read metadata per file instead.

Optional upload fields

A field the submitter left empty is still sent: PHP fills $_FILES with UPLOAD_ERR_NO_FILE, which is recorded as an error, so isValid() returns false and upload() throws. Count the collection before validating to tell "nothing was chosen" apart from "what was chosen is unacceptable":

Multi-file upload

The $_FILES key drops the brackets: new File('photos', $storage). File acts as a collection of the individual files, so count(), foreach and array offsets all work. A file that failed to transfer (too large, nothing selected) is left out, and its error message is already in getErrors().

Individual files are reachable by offset, but the offsets are the collection's own rather than the form's: a file that failed to transfer is left out and everything after it moves up, so $file[0] is the first file that arrived and not necessarily the first input on the page. Reading an offset that is not there gives null. Where your own key has to survive, build the collection with FileList and read getSourceKeys().

Storing only the files that passed

upload() is all-or-nothing: if any file fails validation, nothing is stored. Call uploadValid() in its place to store each file that passed. It returns false when at least one was rejected.

Nothing throws for a rejected file, so the false return is the only signal that one was. A file that never transferred counts as rejected. The files that passed are already on disk, so undo them if you abandon the request there.

Storage failures (destination exists, blocked extension, disk full) still throw part-way through the batch: getUploadedLocators() lists what was written before the throw.

Uploads from another source

File reads $_FILES. FileList takes the files directly, so they can come from a PSR-7 request, a base64 payload in a JSON body, a worker runtime (RoadRunner, Swoole, Bref, Octane), a test harness, or an upload reassembled from chunks:

It extends File, so everything after construction is unchanged: validations, callbacks, isValid(), upload(), uploadValid(), getUploadedLocators(), count(), foreach and offsets.

Argument
array $fileInfos One FileInfoInterface per file, in a flat array. A nested array throws InvalidArgumentException, as does anything else that is not a FileInfoInterface. PSR-7's getUploadedFiles() is a tree, so flatten it first. Keys are not used as offsets.
StorageInterface $storage As File takes it.
array $failures = [] [$clientFilename, UPLOAD_ERR_*] pairs for files that never arrived. Each becomes a getErrors() entry in the same words as the $_FILES path, and counts as a rejection for uploadValid(). Keys are discarded.

getSourceKeys() returns the key each file arrived under, by collection offset:

File::formatUploadFailure($clientFilename, $errorCode) renders that getErrors() entry for a failed transfer outside any FileList. It returns a finished string, so passing it back into $failures raises InvalidArgumentException.

Two decisions this path needs from you

On the $_FILES path PHP guarantees the file arrived in this request's multipart/form-data body. It checks it twice: is_uploaded_file() on the way in, move_uploaded_file() on the way out. That is what stops a manipulated path storing /etc/passwd or another user's upload as a file of your own. Off that path PHP cannot make the guarantee, so both ends refuse the file until you replace them, and every upload fails with This file was not received as an upload.

1. Say where the file came from. Override isUploadedFile(), usually to check the path is inside a directory only your bridge writes to:

return true; disables the check. Only do that where nothing outside your own code can influence the path.

2. Say you are willing to store it.

Nothing else about the write changes: the staged write, the reservation, the extension deny-list, the symlink refusals and the mode all still apply. acceptsFilesNotUploadedByPhp() reads the setting back.

Cleaning up the temporary files

Storing a file moves it, so the tmp files behind stored uploads are gone afterwards. The ones behind rejected uploads are not, and nothing else will remove them:

Put it in a finally around the validating and storing. Nothing else removes these files, so an exception on the way past leaks every one of them.

Bridges

A bridge is caller code: write or decode the incoming file to a tmp path, wrap that path in the TmpUploadFile above, and hand the result to FileList. No dependency, interface or type hint is added for either of the two below, and each page's snippets are read out of the Markdown and run against this library on every push.

Source
PSR-7 request $request->getUploadedFiles(), flattened out of the tree it arrives in, with each stream written to a tmp file under a byte cap. Run against nyholm/psr7 and guzzlehttp/psr7 by composer psr7-readme.
base64 in a JSON body {"filename": "avatar.png", "data": "data:image/png;base64,…"}, decoded under a cap on the encoded length, with the payload's own media type read for nothing. Run by composer base64-docs.

Both end at the same place: a FileList you validate, upload and clean up exactly as above.

Lifecycle callbacks

Four optional hooks fire once per file, each receiving that file's FileInfoInterface: beforeValidate, afterValidate, beforeUpload and afterUpload. Use them for per-file work like renaming or audit logging without writing your own loops.

The two validation hooks are a matched pair: afterValidate fires for every file beforeValidate fired for, including one that failed, so they can safely open and close a per-file resource. The upload hooks are not a pair: a storage failure throws out of upload() before afterUpload runs. Under uploadValid() the upload hooks fire only for the files being stored; the validation hooks still fire for every file.

beforeUpload runs after validation, not before it, so a name set there is never validated: only the storage deny-list and FileSystem's filename rules apply. setName() is safe there, since it cannot change the extension. setExtension() and setNameWithExtension() are not: given anything derived from user input, they can store a file under an extension your validations would have rejected, and the deny-list covers only the formats below.

Rename in beforeValidate if the final name has to be the validated one.

Extending the library

ValidationInterface decides whether a file is acceptable; StorageInterface decides where it lands. Reject a file by throwing GravityPdf\Upload\Exception; say where one went by returning a locator. docs/extending.md works through both, including what a validator of your own may put in front of an end user and the Filename predicates a storage backend needs to reproduce the shipped refusals.

Translating error messages

No translations ship. With no translator installed, every message is the English string it has always been. The English string is the message id, so there is nothing to map — install a callable with Translation::setTranslator() and getErrors() is looked up through it. Nothing else is: Exception::getMessage() stays English for your log.

docs/translation/ covers the hook, the catalogue, the Translation::__() marker and what a broken translation cannot do, with a working adapter for Symfony, Laravel, php-gettext and WordPress.

Reacting to a failure rather than showing it

To branch on why a file was rejected, read the error code rather than the message. Codes are stable across releases; wording is not:

Each entry holds code, the untranslated message_id and its args, the sanitized filename (null if the failure was not about one file), and message, the finished line getErrors() returns. Use message_id and args to write the message yourself, without this library's translator:

Guard that sprintf() if you do not control the catalogue. On PHP 8 a translation whose placeholders do not match throws ArgumentCountError. Translation::render() handles this and falls back to English.

Exception::getErrorCode() returns the same codes, including for storage failures.

Security notes

Prefer FileType over Mimetype and Extension separately. Those two check independent lists, so content sniffed as image/gif stored as avatar.png satisfies both. FileType requires the extension and the contents to describe the same format.

Generate storage names server-side. Sanitizing normalizes client names, so report.txt and report!.txt both land on report.txt. A server-side name avoids the collision and the predictable destination:

Sanitizing is not escaping. Unsafe characters are rewritten, not escaped. A sanitized name, and every string in getErrors(), still needs escaping on output and binding as a query parameter.

Show getErrors(), log the exception. Every string in getErrors() is sanitized and describes the submitted file. A storage exception message is sanitized too, but it is not written for the person who uploaded: it distinguishes a name that already exists from a destination that could not be created, which tells whoever submitted the file what is in your upload directory. Log it and show something generic.

Call isValid() before reading metadata. It performs the is_uploaded_file() check; the metadata accessors do not. This matters where $_FILES is rebuilt by something other than the PHP SAPI (PSR-7 bridges, test harnesses, middleware). On the FileList path that check is one you wrote, so metadata is only as trustworthy as it is.

Serve uploads from a directory the web server won't execute. The storage defaults below are backstops, not a substitute for that.

Extensions blocked by default

FileSystem refuses to write these, whatever the validations allowed. The check runs against the sanitized extension about to be written and throws \GravityPdf\Upload\Exception rather than recording a validation error.

Dots inside the name are not extension separators: FileInfo::setName() rewrites them to hyphens, so release.config.zip is stored as release-config.zip. upload() checks every dot-separated component regardless, for names reaching it from a FileInfoInterface of your own.

Group Extensions
PHP php php2 php3 php4 php5 php6 php7 php8 phps phtml phtm phar pht inc
Server-side includes shtml shtm stm
Apache mod_asis asis
CGI and scripts cgi fcgi pl py rb sh bash ps1 erb rhtml
Java jsp jspx jspf jsw jsv jshtml jar war
ASP / ASP.NET asp aspx asa asax ascx ashx asmx cer cshtml vbhtml
Legacy IIS htr idc printer
ColdFusion cfm cfml cfc
Windows binaries exe dll com bat cmd msi scr vbs ws wsf hta
Server configuration htaccess htpasswd ini conf config
Markup and script html htm xhtml xht xhtm svg svgz xml xsl xslt js mjs swf mht mhtml

Every group but the last is FileSystem::EXECUTABLE_EXTENSIONS, which a server runs. The last is FileSystem::MARKUP_EXTENSIONS, which a browser renders. Serving one from your own origin is stored XSS; SVG is in that group because it carries <script> and event handlers.

The list is not exhaustive. To extend rather than replace it:

To accept SVG, drop just those two entries rather than the whole markup group, and sanitize the file contents yourself:

Entries are matched one dot-separated component at a time, and an entry containing dots is split the same way, so tar.gz blocks tar and gz rather than nothing at all. A leading dot is accepted and removed. A custom list is read the same way, so a dotted entry in it covers more components than it names. getBlockedExtensions() reports what a call configured.

API reference

docs/api-reference.md lists every public method, what it takes and what it throws. The entry points:

Class
File Reads $_FILES[$key] into a collection: validations, callbacks, upload() and uploadValid().
FileList The same collection, built from files you supply.
FileInfo The per-file value object — name, extension, sniffed media type, size, hash, dimensions.
Storage\FileSystem The shipped backend: where a file lands, and the four protections it applies on the way.
Validations FileType and Size, plus the two classes deprecated in 4.0.
Filename The filename rules both layers read, and the predicates for reproducing them.
ErrorCode What a failure carries, and the stable code to branch on.

Authors

License

MIT Public License


All versions of upload with dependencies

PHP Build Version
Package Version
Requires php Version ^7.3 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0
ext-fileinfo Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package gravitypdf/upload contains the following files

Loading the files please wait ...