donatorsky / vendor-compress
Compress vendor directory into one vendor.phar file
Installs: 2 204
Dependents: 0
Suggesters: 0
Security: 0
Stars: 6
Watchers: 1
Forks: 1
Open Issues: 1
Requires
- php: >=7.1
- ext-json: *
- ext-phar: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.14.0
- fzaninotto/faker: ^1.8
- jangregor/phpstan-prophecy: ^0.2.0
- phpstan/phpstan: ^0.10.7
- phpstan/phpstan-phpunit: ^0.10.0
- phpunit/phpunit: ^7.5
- roave/security-advisories: dev-master
- symfony/finder: ^4.2
This package is auto-updated.
Last update: 2024-12-12 23:50:03 UTC
README
Compress vendor directory into one vendor.phar file.
This is a project completely for fun and as a PoC. It is rather unusual to have to compress the vendor directory to save a few megabytes.
The idea for this project was born recently, when I had to work in a very restricted environment (server with 10 MiB disk space for a visit card page) and every byte counted. I decided to share this solution, maybe it will be useful to someone.
The package is created as minimal as possible.
What does it do?
Basically, it packs the content of vendor/
directory into single PHAR file, optionally compressing it and performing other disk-space optimizations. And tries to make all of it working :)
The original vendor/
directory is not removed nor modified!
How to use
Generate vendor.phar (or vendor.phar.gz or vendor.phar.bz2 depending on configuration; see below) file:
bin/vendor-compress [options] [<path>]
Arguments:
Options:
Then use it:
// Replace require __DIR__ . 'vendor/autoload.php'; // With (add .gz/.bz2 if archive-compressed) require __DIR__ . 'vendor.phar'; // Or replace with auto-detection of proper vendor source is_file(__DIR__ . 'vendor.phar') ? require __DIR__ . 'vendor.phar' : require __DIR__ . 'vendor/autoload.php';
Configuration
For easier configuration managing, You can create dedicated file that will be loaded. It must return an instance of \Donatorsky\VendorCompress\Config
class. There is one here (see .vendor_compress.dist
) that works as a fallback if no configuration was provided:
<?php declare(strict_types=1); use Donatorsky\VendorCompress\Config; use Donatorsky\VendorCompress\FileFilters\BasenameFilter; use Donatorsky\VendorCompress\FileFilters\BasenameRegexFilter; use Donatorsky\VendorCompress\FileFilters\ExtensionFilter; use Donatorsky\VendorCompress\FileFilters\IsDirectoryFilter; use Donatorsky\VendorCompress\FileFilters\IsFileFilter; use Donatorsky\VendorCompress\FileFilters\VendorPackageFilter; use Donatorsky\VendorCompress\FileProcessors\MinifyJsonFileProcessor; use Donatorsky\VendorCompress\FileProcessors\StripWhitespacesPhpFileProcessor; return Config::create() ->setFilesCompressionMethod(Phar::NONE) // It is also default ->setArchiveCompressionMethod(Phar::NONE) // It is also default ->setExcluded([ new VendorPackageFilter('donatorsky', 'vendor-compress'), new IsDirectoryFilter(new BasenameFilter('docs')), new IsDirectoryFilter(new BasenameFilter('tests')), new IsFileFilter(new BasenameFilter('composer.json')), new IsFileFilter(new BasenameFilter('composer.lock')), new IsFileFilter(new BasenameFilter('.gitignore')), new IsFileFilter(new BasenameRegexFilter('/^(?:README|CHANGELOG|phpunit.*\.xml.*$)/i')), ]) ->addFileProcessor(new MinifyJsonFileProcessor(), new ExtensionFilter('json')) ->addFileProcessor(new StripWhitespacesPhpFileProcessor(), new ExtensionFilter('php'));
You can create your own .vendor_compress
or .vendor_compress.dist
configuraton file in working directory (usually project's root directory) or specify path to the configuration file using --configuration
option. Tthe order of loading configurationis:
- One specified via
--configuration
. If does no exist, command will fail. Current Working Directory (CWD)/.vendor_compress
,CWD/.vendor_compress.dist
,vendor/donatorsky/vendor-compress/.vendor_compress.dist
).
Available configuration options
Below is the list of all available options.
setFilesCompressionMethod(int $filesCompressionMethod)
Allows to set compression method that will be used for compressing each file individually. $filesCompressionMethod
should be one of supported compressions available via \Phar::CONSTANT
constant.
setArchiveCompressionMethod(int $archiveCompressionMethod)
Allows to set compression method that will be used for compressing whole file. The difference between setFilesCompressionMethod()
is that with this option, the whole archive will be compressed at once at the end. It may be more efficient to do this in that way (see comparison below). $archiveCompressionMethod
should be one of supported compressions available via \Phar::CONSTANT
constant.
setAlias(string $alias)
Allows to alias with which generated Phar archive should be referred to in calls to stream functionality. See more at: https://secure.php.net/manual/en/phar.construct.php.
addExcluded(\Donatorsky\VendorCompress\Contracts\FileFilterInterface $fileFilter)
Allows to add file/directory exclusion rule used for vendor files list generation. There are already some rules included (see src/FileFilters/
) that You can use, or write Your own.
setExcluded(\Donatorsky\VendorCompress\Contracts\FileFilterInterface[] $fileFilters)
Allows to set multiple file/directory exclusion rules at once. This overrides existing list of rules.
addIncluded(\Donatorsky\VendorCompress\Contracts\FileFilterInterface $fileFilter)
Allows to add file/directory inclusion rule used for vendor files list generation. There are already some rules included (see src/FileFilters/
) that You can use, or write Your own. Inclusion rules have higher priority than exclusions. However there is something tricky worth mentioning:
- By default, all content of the
vendor/
directory is included. - Excluding eg. path
vendor-name/package-name
will exclude content of this directory. - Including
vendor-name
directory will override exclusion from above. - Including
vendor-name/package-name/subdirectory
will not work, asvendor-name/package-name
is already excluded (it is due to how RecursiveDirectoryIterator with RecursiveCallbackFilterIterator works). TODO for fixing in future :) - Let's assume, that there is
vendor-name/package-name/important_file.php
. Including it will not work either.
setIncluded(\Donatorsky\VendorCompress\Contracts\FileFilterInterface[] $fileFilters)
Allows to set multiple file/directory inclusion rules at once. This overrides existing list of rules.
addFileProcessor(\Donatorsky\VendorCompress\Contracts\FileProcessorInterface $fileProcessor, \Donatorsky\VendorCompress\Contracts\FileFilterInterface $fileFilter, ...$moreFileFilters)
There are also file processors. They define some file content manipulations that should be performed before it is added to the archive. Eg. You can minify PHP code. It is possible to define multiple file rules to which content manipulation should be applied to.
Compression ratio comparison
Initial vendor/
size: 43.348 MiB
Number of files: 9664
Number of files after exclusions: 6992
Note: Only by removing docs and tests (mainly) the size of vendor decreases by ~38%.
TODO: Add some generation times to comparison above.
Exit codes
All exit codes constants can be found in bin/constants.php
file.
Tips for use
- The PHAR file tries to mount directories outside of the
vendor/
directory and is currently only tested for paths directly one level above thevendor/
directory. If you rely in some way on paths from outside of the root directory of the project, autoload may not work. - I think that the most useful would be to generate autoloading with the option
--classmap-authoritative
for production. To make autoloading from a PHAR file work, I rewrite the generated file indexes, so it may not work for dynamic file guessing and searching. - Watch out when using whole archive compression. Although it provides better compression ratio, it also comes with additional hack for decompressing files (overrides custom
stub.php
script) which might break autoloading. Files compression is a bit more reliable here.
Plans for future
- Remove unnecessary whitespaces (a.k.a finish
\Donatorsky\VendorCompress\FileProcessors\StripWhitespacesPhpFileProcessor
) - Maybe it is good idea to use symfony/console (originally it was used)?