Download the PHP package italix/storage without Composer

On this page you can find all versions of the php package italix/storage. 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 storage

Italix Storage

PHP Version

File storage where the caller never chooses the filename.

Zero Composer dependencies; ext-fileinfo for sniffing, ext-mbstring for the display name.


The safety model

The stored path is {folder}/{shard}/{sha256}.{extension} where:

A pleasant side effect: identical content stores once. Re-uploading the same document is free.

A whitelist, not a blacklist

The direction is the property. A blacklist has to anticipate every dangerous extension — .php, .phtml, .php5, .phar, .htaccess, whatever the next web server adds — and is wrong the moment one is missed. A whitelist is wrong only by refusing something harmless, which is a support ticket rather than a compromise.

Three types are refused with a stated reason, so a reader sees the omission is deliberate:

why
image/svg+xml SVG can contain script
text/html HTML served from an upload directory is stored XSS
application/xml entity expansion in whatever parses it later

The folder is checked three times

Character whitelist, explicit .. refusal, and a resolved path verified to be inside the root. Two of those should be unreachable. The cost of being wrong is arbitrary file write, which is worth one redundant string comparison.


Validating an upload before storing it

The vocabulary already existed, so this is two checks rather than a new engine (italix/rules 1.1.0):

Both read the file on disk — the type from finfo, the size from filesize(). A rule trusting $_FILES['type'] would be checking a string the uploader chose.

Storing then validates again: put() refuses an unlisted type whatever the form said. Validation gives the visitor a message they can act on; the store gives the guarantee.


Choosing what may be stored

The whitelist is compulsory and restrictive by default. Which types are on it is yours:

Groups: images, documents, office, open_document, audio_video, archives, fonts, ebooks.

Catalogues are immutable — with() returns a new one — so a catalogue handed to a store cannot be widened afterwards by code elsewhere.

Choosing where files land

HashedPath sizes against listing cost, not read cost — see its docblock for the measurements. BucketedPath solves the other problem: the directory that holds the folders. A folder is still exactly one subtree, which is what keeps "move this tenant elsewhere" an rsync of one path.

A strategy decides where, never whether: its output is verified to stay inside the root exactly like a folder is, so a third-party strategy is never a security decision.

Large files

Measured on a 74 MB file: put() peaks at 4 MB, read() at 78 MB. read() and put_contents() remain, and are fine for an avatar — they are the convenience, not the primary route.

More than one volume

Lazy: a callable is not invoked until that store is asked for. Moving a corpus to another disk or host is a subtree copy plus a config change, because a stored path is relative to a root and never absolute. If you store absolute paths, none of this works.

What ships: LocalStore, and nothing else. There is no S3, SFTP or Google Drive driver in this package, and StoreRegistry does not imply one — it names stores, it does not conjure them. What 2.0.0 added is the seam that makes such a driver writable without changing core: put_stream(), read_stream() and temporary_url(). See DRIVERS.md for the contract, and for an honest assessment of which backends fit — S3-compatible stores fit well, SFTP fits, FTP barely, and Google Drive does not fit at all, for a reason worth reading before trying.

How deep the tree goes

{folder}/{ab}/{cd}/{sha256}.{ext} — two levels, 65,536 leaf directories, configurable 1-4 via the third constructor argument.

Measured on ext4 with dir_index:

entries in a directory is_file() ×2000 scandir()
100 5.1 ms 0.2 ms
10,000 6.6 ms 15.6 ms
100,000 12.0 ms 375.5 ms

Looking a known path up barely degrades — that is what the htree is for. Listing degrades ~1,800x, and listing is what backups, rsync, du and deletion sweeps do. So the depth is chosen to keep a leaf small, not to speed up reads.

depth directories at 1M files at 16M files
1 256 3,906 each 62,500 each
2 (default) 65,536 15 each 244 each
3 16,777,216 0.06 each 1 each

Depth 3 is the tempting mistake: an empty directory costs 4 KB and an inode, so 16.7M of them is ~67 GB of metadata before storing any content, to hold about one file each.

Changing the depth is not a migration. Every read path takes the stored path as an argument and never recomputes it, so files written under the old layout keep resolving from the path already in the database. The only cost is that re-uploading already-stored content writes a second copy rather than de-duplicating against the old one.

Readable URLs without giving up the safety model

The store will not let an uploader name a file on disk. That does not mean links have to be ugly — the path and the URL are different concerns, and only the first one is a safety property.

The route looks the file up by the handle and never reads the slug. Change the slug and the file still resolves — or answer 301 to the canonical form, which a search engine prefers anyway. Because the lookup ignores it, the slug can be readable without becoming an attack surface: it is output, and output is italix/encode's problem.

A name that slugifies to nothing falls back to the handle, so it is never empty:

Original name slug()
Summer Vacation — Beach Sunset (Edited).JPG summer-vacation-beach-sunset-edited
Café au Lait — Recipe (Final).pdf cafe-au-lait-recipe-final
../../etc/passwd 9f2c8a1b0000 (the handle)
"" 9f2c8a1b0000 (the handle)

Worth being honest about the payoff: a filename is a weak signal for image search. Alt text, surrounding copy and the page title matter more. The larger win is that a human reading a URL, a log line or a download dialog can tell what the file is.

Deliberately not


All versions of storage with dependencies

PHP Build Version
Package Version
Requires php Version >=7.4
ext-fileinfo Version *
ext-mbstring 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 italix/storage contains the following files

Loading the files please wait ...