You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
spip_loader/compiler/PharArchive.php

155 lines
4.8 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?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 larchive */
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;
}
}