Skip to content

Commit

Permalink
first release
Browse files Browse the repository at this point in the history
  • Loading branch information
ms-pxxl committed Mar 15, 2018
1 parent 04365fa commit 5cd6591
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 4 deletions.
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
composer.phar
vendor.phar
.idea
/vendor/

# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Paerius
=======

Bundles your vendor directory into a single PHP Archive (PHAR).

## Requirements

`php.ini` edit phar settings:

; http://php.net/phar.readonly
phar.readonly = Off
; http://php.net/phar.require-hash
phar.require_hash = Off



## Installation

Add Pmt to your project using Composer:

composer require axute/paerius

## Files that will be deleted

See [Paerius.php](src/Paerius.php) at class fields for files that will be deleted (in vendor path).



## Usage

Run the binary to create/update your PHAR file:

php vendor/bin/paerius

### Compression

Files in phar archive will be tried to compress.

PHP Extension is required (optionaly).


force special compression (bz2/gz/none)

default is bzip2 (if installed, fallback gzip if installed, fallback none)

bzip2 < gzip < none

php vendor/bin/paerius bz

php vendor/bin/paerius gz

php vendor/bin/paerius none


A new file named `vendor.phar` will be added to your working directory (project root).
Update your bootstrap to include `./vendor.phar` instead of `vendor/autoload.php` and you're good to go.

$autoload = require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor.phar';
$autoload->addPsr4('[YOUR NAMESPACE]',__DIR__.DIRECTORY_SEPARATOR.'src');

### Automate

To generate `vendor.phar` automaticly, add the following lines to **your** composer.json

"scripts": {
"post-install-cmd": "vendor/bin/paerius",
"post-update-cmd": "vendor/bin/paerius"
}
6 changes: 6 additions & 0 deletions bin/paerius
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env php
<?php

require_once getcwd().DIRECTORY_SEPARATOR .'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
$aerius = new \Paerius\Paerius(getcwd());
$aerius->build($argv[1] ?? null);
33 changes: 33 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "axute/paerius",
"description": "Bundles your vendor directory into a PHP Archive (PHAR)",
"version": "0.9",
"license": "MIT",
"authors": [
{
"name": "axute",
"email": "[email protected]"
}
],
"keywords": [
"composer",
"phar",
"vendor"
],
"suggest": {
"ext-bzip2": "*",
"ext-gz": "*"
},
"homepage": "https://github.com/axute/paerius",
"require": {
"php": ">=7.2"
},
"autoload": {
"psr-4": {
"Paerius\\": "src"
}
},
"bin": [
"bin/paerius"
]
}
19 changes: 19 additions & 0 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

178 changes: 178 additions & 0 deletions src/Paerius.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

namespace Paerius;

use FilesystemIterator;
use Phar;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIterator;
use RecursiveIteratorIterator;
use RuntimeException;
use SplFileInfo;

class Paerius {

public const DEFAULT_BASENAME = 'vendor.phar';

/** @var string */
protected $baseName;

public static $toDeleteBasenames = ['LICENSE', '.styleci.yml', '.travis.yml', '.gitattributes', 'phpunit.xml.dist', 'README.rst', 'CHANGELOG', 'CHANGES', 'README', 'VERSION', '.php_cs.dist', '.php_cs', '.editorconfig', 'php_cs.xml', 'phpunit.xml', 'phpcs.xml', '.codecov.yml', 'AUTHORS', 'Makefile', '.gitignore'];

public static $toDeleteDirectoryNames = ['.git', 'tests', 'Tests', 'test', 'doc', 'testing', 'test_old', '.svn', '.cvs', '.idea', '.DS_Store', '.hg'];

public static $toDeleteExtensions = ['md', 'gitignore', 'markdown', 'hprof', 'pyc'];

/** @var string */
protected $vendorPath;

/** @var string */
protected $workingPath;

/**
* Aerius constructor.
*
* @param string $workingPath
* @param null|string $baseName
* @throws \RuntimeException
*/
public function __construct(string $workingPath, ?string $baseName = self::DEFAULT_BASENAME) {
$workingPath = rtrim($workingPath, DIRECTORY_SEPARATOR);
$this->setWorkingPath($workingPath);
$this->setVendorPath($workingPath . DIRECTORY_SEPARATOR . 'vendor');
$this->setBaseName($baseName);
}

/**
* @param string|null $forceCompression gz*|bz*|none, null = automatic detection bz < gz < none
* @return bool
* @throws \UnexpectedValueException
* @throws \BadMethodCallException
*/
public function build(?string $forceCompression): bool {
$pharFilePath = $this->getWorkingPath() . DIRECTORY_SEPARATOR . $this->getBaseName();
if (is_file($pharFilePath)) {
unlink($pharFilePath);
}
$this->cleanupVendorDir();
$phar = new Phar($pharFilePath, FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME, $this->baseName);
$phar->setMetadata(
[
'datetime' => date('c'),
]
);
$phar->setSignatureAlgorithm(Phar::SHA1);
$phar->buildFromDirectory($this->getVendorPath());
$supportedCompression = Phar::getSupportedCompression();
if (($forceCompression !== null && stripos($forceCompression, 'bz') === 0) || ($forceCompression === null && \in_array('BZIP2', $supportedCompression, true))) {
$phar->compressFiles(Phar::BZ2);
}
else if (($forceCompression !== null && stripos($forceCompression, 'gz') === 0) || ($forceCompression === null && \in_array('GZ', $supportedCompression, true))) {
$phar->compressFiles(Phar::GZ);
}

return $phar->setStub("<?php\n\\Phar::mapPhar();\nreturn require 'phar://{$this->baseName}/autoload.php';\n__HALT_COMPILER();");
}

/**
* remove all files from directory
*
* @return $this
*/
protected function cleanupVendorDir(): self {
$file_del = new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(new RecursiveDirectoryIterator($this->vendorPath), [$this, 'filter'])
);

foreach ($file_del as $toDlete) {
/** @var \SplFileInfo $toDlete */
self::remove($toDlete->getPath() . DIRECTORY_SEPARATOR . $toDlete->getFilename());
}

return $this;
}

public function filter( SplFileInfo $current, $key, RecursiveIterator $iterator ): bool {
// Allow recursion
if ($iterator->hasChildren()) {
return true;
}
$entry = $current->getFilename();
if (\in_array($entry, ['.', '..'], true) === false) {
$path = $current->getPath();
$ext = strtolower(pathinfo($entry, PATHINFO_EXTENSION));
$dirnames = explode(DIRECTORY_SEPARATOR, trim(substr($path, \strlen($this->getVendorPath())), DIRECTORY_SEPARATOR));
if (\count(array_intersect($dirnames, self::$toDeleteDirectoryNames)) > 0) {
return true;
}

return \in_array($ext, self::$toDeleteExtensions, true) || \in_array($entry, self::$toDeleteBasenames, true);
}

return false;
}

public function getBaseName(): string {
return $this->baseName;
}

public function getVendorPath(): string {
return $this->vendorPath;
}

public function getWorkingPath(): string {
return $this->workingPath;
}

protected static function remove(string $src): void {
if (is_dir($src)) {
$dir = opendir($src);
while (false !== ($file = readdir($dir))) {
if (($file !== '.') && ($file !== '..')) {
self::remove($src . '/' . $file);
}
}
closedir($dir);
rmdir($src);
}
else if (is_file($src)) {
unlink($src);
}
}

protected function setBaseName(string $baseName): self {
$this->baseName = $baseName;

return $this;
}

/**
* @param string $vendorPath
* @return Paerius
* @throws \RuntimeException
*/
protected function setVendorPath(string $vendorPath): self {
if (!is_dir($vendorPath)) {

throw new RuntimeException("$vendorPath is not a directory!");
}
$this->vendorPath = $vendorPath;

return $this;
}

/**
* @param string $workingPath
* @return Paerius
* @throws \RuntimeException
*/
protected function setWorkingPath(string $workingPath): self {
if (!is_dir($workingPath)) {
throw new RuntimeException("{$workingPath} is not a directory");
}
$this->workingPath = $workingPath;

return $this;
}
}

0 comments on commit 5cd6591

Please sign in to comment.