Skip to content

Commit

Permalink
[Bard] Adding ability to compile into a phar file (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaEstes authored Oct 3, 2024
1 parent cab5a4f commit 5b8c9ed
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ vendor/
/.php-cs-fixer.php
/.phpunit.result.cache
/.phpunit
bard.phar
composer.lock
phpunit.xml
composer.phar
Expand Down
12 changes: 12 additions & 0 deletions src/SonsOfPHP/Bard/bin/compile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env php
<?php

require __DIR__.'/../vendor/autoload.php';

use SonsOfPHP\Bard\Compiler;

error_reporting(-1);
ini_set('display_errors', '1');

$compiler = new Compiler();
$compiler->compile();
135 changes: 135 additions & 0 deletions src/SonsOfPHP/Bard/src/Compiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Bard;

use Symfony\Component\Finder\Finder;

/**
* @author Joshua Estes <joshua@sonsofphp.com>
*/
final class Compiler

Check failure on line 12 in src/SonsOfPHP/Bard/src/Compiler.php

View workflow job for this annotation

GitHub Actions / Psalm (8.2)

UnusedClass

src/SonsOfPHP/Bard/src/Compiler.php:12:13: UnusedClass: Class SonsOfPHP\Bard\Compiler is never used (see https://psalm.dev/075)
{
public function compile(string $pharFile = 'bard.phar'): void
{
if (file_exists($pharFile)) {
unlink($pharFile);
}

$phar = new \Phar($pharFile, 0, 'bard.phar');
$phar->setSignatureAlgorithm(\Phar::SHA512);
$phar->startBuffering();

// >>>
// Add source files
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->notPath('Tests')
->notName('Compiler.php')
->in(__DIR__)
;

/** @var \Symfony\Component\Finder\SplFileInfo $file */

Check failure on line 35 in src/SonsOfPHP/Bard/src/Compiler.php

View workflow job for this annotation

GitHub Actions / Psalm (8.2)

UnnecessaryVarAnnotation

src/SonsOfPHP/Bard/src/Compiler.php:35:18: UnnecessaryVarAnnotation: The @var Symfony\Component\Finder\SplFileInfo annotation for $file is unnecessary (see https://psalm.dev/212)
foreach ($finder as $file) {
$this->addFile($phar, $file);
}

// <<<

// >>>
// Add vendor files
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->notPath('composer.json')
->notPath('composer.lock')
->notPath('phpunit.xml.dist')
->notPath('*.md')
->notPath('docs')
->notPath('Tests')
->notPath('tests')
->in(__DIR__ . '/../vendor')
;
/** @var \Symfony\Component\Finder\SplFileInfo $file */

Check failure on line 56 in src/SonsOfPHP/Bard/src/Compiler.php

View workflow job for this annotation

GitHub Actions / Psalm (8.2)

UnnecessaryVarAnnotation

src/SonsOfPHP/Bard/src/Compiler.php:56:18: UnnecessaryVarAnnotation: The @var Symfony\Component\Finder\SplFileInfo annotation for $file is unnecessary (see https://psalm.dev/212)
foreach ($finder as $file) {
$this->addFile($phar, $file);
}

// <<<

$this->addBin($phar);
$this->setStub($phar);
$phar->stopBuffering();
}

private function addFile(\Phar $phar, \SplFileInfo $file): void
{
$contents = file_get_contents((string) $file);
$contents = $this->stripeWhitespace($contents);
if ('LICENSE' === $file->getFilename()) {
$contents = "\n" . $contents . "\n";
}

$phar->addFromString($this->getLocalName($file), $contents);
}

private function stripeWhitespace(string $contents): string
{
$output = '';
foreach (token_get_all($contents) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], [T_COMMENT, T_DOC_COMMENT])) {
$output .= '';
} elseif (T_WHITESPACE === $token[0]) {
$whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
$whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
$whitespace = preg_replace('{\n +}', "\n", $whitespace);
$whitespace = preg_replace('{\n}', '', $whitespace);
$output .= $whitespace;
} else {
$output .= $token[1];
}
}

return $output;
}

private function getLocalName(\SplFileInfo $file): string
{
$realPath = $file->getRealPath();
$pathPrefix = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR;
$pos = strpos($realPath, $pathPrefix);

$relativePath = ($pos !== false) ? substr_replace($realPath, '', $pos, strlen($pathPrefix)) : $realPath;

return strtr($relativePath, '\\', '/');
}

private function addBin(\Phar $phar): void
{
$contents = file_get_contents(__DIR__ . '/../bin/bard');
$contents = preg_replace('{^#!/usr/bin/env php\s*}', '', $contents);

$phar->addFromString('bin/bard', $contents);
}

private function setStub(\Phar $phar): void
{
$phar->setStub($this->getStub());
}

private function getStub(): string
{
return <<<'STUB'
#!/usr/bin/env php
<?php
Phar::mapPhar('bard.phar');
require 'phar://bard.phar/bin/bard';
__HALT_COMPILER();
STUB;
}
}

0 comments on commit 5b8c9ed

Please sign in to comment.