|
|
<?php
|
|
|
|
|
|
namespace Spip\Loader\Compiler;
|
|
|
|
|
|
use Composer\Pcre\Preg;
|
|
|
use IteratorAggregate;
|
|
|
use Seld\PharUtils\Timestamps;
|
|
|
use Symfony\Component\Finder\Finder;
|
|
|
|
|
|
class PharArchive {
|
|
|
private const FILENAME = 'spip_loader.phar';
|
|
|
|
|
|
public function __construct(
|
|
|
private string $sourceDirectory,
|
|
|
private string $buildDirectory,
|
|
|
private string $filename = self::FILENAME,
|
|
|
private bool $debug = false,
|
|
|
) {
|
|
|
}
|
|
|
|
|
|
public function getPhpFiles(): IteratorAggregate {
|
|
|
return (new Finder())->files()
|
|
|
->in($this->sourceDirectory)
|
|
|
->name(['*.php', '*.php8'])
|
|
|
# /!\ Méga attention, ça enlèvera aussi les mêmes répertoires
|
|
|
# dans des sous dossiers https://github.com/symfony/symfony/issues/28158
|
|
|
->exclude(['phar/', 'tests/']);
|
|
|
}
|
|
|
|
|
|
public function getCssFiles(): IteratorAggregate {
|
|
|
$order = [
|
|
|
'minipage.vars.css',
|
|
|
'reset.css',
|
|
|
'clear.css',
|
|
|
'minipage.css',
|
|
|
'minipres.css',
|
|
|
'app.css',
|
|
|
];
|
|
|
return (new Finder())->files()
|
|
|
->in($this->sourceDirectory . '/public/assets')
|
|
|
->name('*.css')
|
|
|
->sort(function ($a, $b) use ($order) {
|
|
|
return array_search($a->getFilename(), $order) <=> array_search($b->getFilename(), $order);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
public function getAssetsFiles(): IteratorAggregate {
|
|
|
return (new Finder())->files()
|
|
|
->in($this->sourceDirectory . '/public/assets')
|
|
|
->notName('*.css');
|
|
|
}
|
|
|
|
|
|
/** @return int Nombre de fichiers insérés dans l’archive */
|
|
|
public function build(
|
|
|
string $public_version,
|
|
|
string $private_version,
|
|
|
\DateTimeInterface $versionDate,
|
|
|
): int {
|
|
|
$destFile = $this->buildDirectory . '/' . $this->filename;
|
|
|
$phar = new \Phar($destFile, 0, $this->filename);
|
|
|
$phar->setSignatureAlgorithm(\Phar::SHA512);
|
|
|
$phar->startBuffering();
|
|
|
$phar->setStub($this->getStub($public_version, $private_version, $versionDate));
|
|
|
$phar['/index.php'] = "<?php require __DIR__ . '/public/index.php';";
|
|
|
|
|
|
foreach ($this->getPhpFiles() as $file) {
|
|
|
$_file = str_replace($this->sourceDirectory . '/', '', $file);
|
|
|
$phar[$_file] = $this->stripWhitespacePhpFile(file_get_contents($file));
|
|
|
$phar[$_file]->compress(\Phar::GZ);
|
|
|
}
|
|
|
|
|
|
$css = '';
|
|
|
foreach ($this->getCssFiles() as $file) {
|
|
|
$css .= $this->stripWhitespace(file_get_contents($file)) . "\n";
|
|
|
}
|
|
|
$phar['/public/assets/all.css'] = $css;
|
|
|
|
|
|
$phar->buildFromIterator($this->getAssetsFiles(), $this->sourceDirectory);
|
|
|
$phar->stopBuffering();
|
|
|
#$phar->compressFiles(\Phar::GZ); # casse les assets
|
|
|
$nb = $phar->count();
|
|
|
|
|
|
$util = new Timestamps($destFile);
|
|
|
$util->updateTimestamps($versionDate);
|
|
|
$util->save($destFile, \Phar::SHA512);
|
|
|
|
|
|
return $nb;
|
|
|
}
|
|
|
|
|
|
public function renameTo(string $filename) {
|
|
|
rename($this->buildDirectory . '/' . self::FILENAME, $this->buildDirectory . '/' . $filename);
|
|
|
}
|
|
|
|
|
|
private function getStub(string $version, string $full_version, \DateTimeInterface $date): string {
|
|
|
$stub = file_get_contents($this->sourceDirectory . '/phar/stub.php');
|
|
|
#$stub = $this->stripWhitespacePhpFile($stub);
|
|
|
$stub = $this->replaceConst($stub, 'VERSION', '', $version);
|
|
|
$stub = $this->replaceConst($stub, 'FULL_VERSION', '', $full_version);
|
|
|
$stub = $this->replaceConst($stub, 'DATE', '', $date->format('Y-m-d H:i:s'));
|
|
|
$stub = $this->replaceConst($stub, 'DEBUG', false, $this->debug);
|
|
|
return $stub;
|
|
|
}
|
|
|
|
|
|
private function replaceConst($stub, $name, $default, $value) {
|
|
|
$search = sprintf('const %s = %s;', $name, var_export($default, true));
|
|
|
$replace = sprintf('const %s = %s;', $name, var_export($value, true));
|
|
|
return str_replace($search, $replace, $stub);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Removes whitespace from a PHP source string while preserving line numbers.
|
|
|
*
|
|
|
* @param string $source A PHP string
|
|
|
* @return string The PHP string with the whitespace removed
|
|
|
*/
|
|
|
private function stripWhitespacePhpFile(string $source): string {
|
|
|
if (!function_exists('token_get_all')) {
|
|
|
return $source;
|
|
|
}
|
|
|
|
|
|
$output = '';
|
|
|
foreach (token_get_all($source) as $token) {
|
|
|
if (is_string($token)) {
|
|
|
$output .= $token;
|
|
|
} elseif (in_array($token[0], [T_COMMENT, T_DOC_COMMENT])) {
|
|
|
$output .= str_repeat("\n", substr_count($token[1], "\n"));
|
|
|
} elseif (T_WHITESPACE === $token[0]) {
|
|
|
// reduce wide spaces
|
|
|
$whitespace = Preg::replace('{[ \t]+}', ' ', $token[1]);
|
|
|
// normalize newlines to \n
|
|
|
$whitespace = Preg::replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
|
|
|
// trim leading spaces
|
|
|
$whitespace = Preg::replace('{\n +}', "\n", $whitespace);
|
|
|
$output .= $whitespace;
|
|
|
} else {
|
|
|
$output .= $token[1];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return $output;
|
|
|
}
|
|
|
|
|
|
private function stripWhitespace(string $content): string {
|
|
|
$lines = explode("\n", $content);
|
|
|
$lines = array_map('trim', $lines);
|
|
|
$lines = array_filter($lines);
|
|
|
|
|
|
$content = implode("\n", $lines);
|
|
|
$content = preg_replace('# \s+#', ' ', $content); # /!\ casse des content: " " (y en a pas)
|
|
|
$content = preg_replace('#\/\*(.*)\*\/#sU', '', $content);
|
|
|
|
|
|
return $content;
|
|
|
}
|
|
|
}
|