FileWrapper
An advanced PHP download wrapper, (*1)
This class provides a PHP wrapper between the client requests and the files on
the server., (*2)
It handles most mime types intelligently and properly responds to client
requests for partial content as well as requests to see if the client cached
copy of the file is still valid., (*3)
Use of this class instead of letting the server just serve the file only makes
sense when the file is outside the server document root or when there are
conditions (such as age verification or other related things) that should be
checked before the file is served., (*4)
The class supports partial content requests and client cache validation. With
partial content requests, at this time only responds with the first partial
content range specified, it ignores the other parts., (*5)
The class also supports requests from the client asking if the version of the
file it has is up to date., (*6)
- Install
- Using the Class
- Example Usage
- Catchable Exceptions
- Extending the Class
- Unit Testing
Install
You can add this to a composer project via:, (*7)
"require": {
"awonderphp/filewrapper": "^1.1"
},
As long as your composer.json
allows the Packagist
repository, that should pull in this library when you run the command:, (*8)
composer install
Manual Installation
For manual installation, there are four class libraries you need to have where
your auto-loader can find them:, (*9)
-
FileWrapper.php
-- This is the class library.
-
InvalidArgumentException.php
-- An exception library.
-
TypeErrorException.php
-- An exception library.
-
NullPropertyException.php
-- An exception library.
All four libraries use the namespace \AWonderPHP\FileWrapper
, (*10)
RPM Installation
I have started a project called
PHP Composer Class Manager
but it is not yet ready for deployment, and as of today (March 06 2018) it will
likely be awhile., (*11)
Using the Class
The class constructor has one required argument and four optional arguments.
The required argument is first, the path on the filesystem to the file being
served. The most basic way to use this class:, (*12)
use \AWonderPHP\FileWrapper\FileWrapper as FileWrapper;
$obj = new FileWrapper('/srv/whatever/foo.mp4`);
The parameters the constructor takes:, (*13)
-
$path
-- Required
The path on the filesystem to the file being served. Always a string
.
-
$request
-- Optional
The name of the requested file that the client will see. This only matters if
it is different than the name of the file on the filesystem and the file is
being served as an attachment for the client to save to its local filesystem.
Set it to null
to just use the name of the file in $path
, otherwise set
it to a string
. Default value is null
.
-
$mime
-- Optional
The MIME type the file should be served with. The class will attempt to sniff
the correct MIME type if set to null
but it is better to explicitly specify
the MIME type. Use a string
to specify a MIME type. To tell the class
detect the mime type, set to null
.
-
$maxage
-- Optional
How long the client should cache the file for. This parameter can either be
an integer representing number of seconds, an integer representing the UNIX
timestamp when you want the cache to expire, a string that can be parsed by
the strtotime()
command specifying when you want the cache to expire, or a
\DateInterval
object specifying how long the browser should cache it for.
The default value is 604800
seconds, which is one week.
-
$attachment
-- Optional
Whether a header should be sent telling the client to save the file. Boolean
default to false
.
There are two public functions available once you have instantiated the class:, (*14)
$obj->setAllowOrigin($origin)
In some cases you may have a need to set the access-control-allow-origin
header. This function allows you to set it. Note that if the class is serving
a font, it automatically sets that header to *
unless you specify otherwise., (*15)
$obj->sendfile()
This causes the file to be sent to the requesting client., (*16)
Example Usage
Image File
This example serves an image file:, (*17)
use \AWonderPHP\FileWrapper\FileWrapper as FileWrapper;
$obj = new FileWrapper('/srv/images/image.jpg');
$obj->sendfile();
exit();
Assuming the file actually is a JPEG image, the class will figure out the file
MIME type is image/jpeg
and serve the file to the requesting client as such., (*18)
Audio Download
This example uses all five parameters to serve an audio file that the client
save to disk:, (*19)
use \AWonderPHP\FileWrapper\FileWrapper as FileWrapper;
$obj = new FileWrapper('/srv/media/549805.mka', 'waterfall.mka', 'audio/x-matroska', 0, true);
$obj->sendfile();
exit();
The file on the server filesystem is named 549804.mka
which is not a very
descriptive name, so we use the second argument to give a more descriptive
file name the end user will benefit from., (*20)
The MIME type is explicitly set to audio/x-matroska
which is helpful to the
client knowing what type of file is being downloaded., (*21)
We set the seconds for caching the file to 0 since it is a download, though
honestly that can be set to null
as the cache time is not applicable to file
download., (*22)
Finally, we use true
as the last argument so that the server sends the right
header to trigger the client to save the file to disk rather than open it in
the browser window., (*23)
Catchable Exceptions
There are three catchable exception classes that accompany this class., (*24)
\AWonderPHP\FileWrapper\InvalidArgumentException
This exception class extends
\InvalidArgumentException
and is thrown when a parameter is of the correct type but does not make sense., (*25)
Currently it is only thrown when a negative $maxage
parameter is used or
when the $maxage
is set using a string that the core PHP strtotime
function
is not able to parse., (*26)
\AWonderPHP\FileWrapper\TypeErrorException
This exception class extends
\TypeError
and is thrown
when a variable set as a parameter is of the wrong type., (*27)
\AWonderPHP\FileWrapper\NullPropertyException
This exception class extends
\ErrorException
and is
thrown when a class property is NULL
that should not be., (*28)
This exception should never happen, if it happens it is due to a bug in the
class., (*29)
Extending the Class
This class also contains some methods useful when dealing with text based
files, such as conversion of non-UTF8 charsets to UTF8, minification of
JavaScript/CSS files, and word-wrapping of plain text files. While those
methods are in this class, they are not enabled, extend the class to enable
them:, (*30)
class TextWrapper extends \AliceWonderMiscreations\Utilities\FileWrapper
{
public function __construct(string $path, $request = null, $mime = null, bool $minify = false)
{
$this->toUTF8 = true;
$maxage = 604800;
if ($minify) {
$this->minify = true;
}
parent::__construct($path, $request, $mime, $maxage, false);
}
}
That extended class turns on conversion to UTF8 and gives the option to minify
the files served with it automatically., (*31)
Unit Testing
I have not yet created any unit tests for this class., (*32)
My personal experience is that unit tests reveal bugs the developer did not
know existed before writing the unit tests. That means there probably are some
bugs in this class., (*33)
That being said, I have been using this class on
Naughty.Audio for JS/CSS minification, as a file
download wrapper, and for HTML5 media with partial content requests., (*34)
It “works for me”., (*35)
Yes, I do need to create actual unit tests. Researching how to do unit tests
for a download wrapper is something I am currently doing, it is not trivial
as it involves interpreting headers sent by the client., (*36)