mirror of
https://github.com/wintercms/winter.git
synced 2024-06-28 05:33:29 +02:00
222 lines
5.2 KiB
PHP
222 lines
5.2 KiB
PHP
<?php namespace System\Classes;
|
|
|
|
use ApplicationException;
|
|
use Config;
|
|
use Winter\Storm\Filesystem\Filesystem;
|
|
use Winter\Storm\Halcyon\Datasource\FileDatasource;
|
|
|
|
/**
|
|
* Stores the file manifest for this Winter CMS installation.
|
|
*
|
|
* This manifest is a file checksum of all files within this Winter CMS installation. When compared to the source
|
|
* manifest, this allows us to determine the current installation's build number.
|
|
*
|
|
* @package winter\wn-system-module
|
|
* @author Ben Thomson
|
|
*/
|
|
class FileManifest
|
|
{
|
|
/**
|
|
* @var string Root folder of this installation.
|
|
*/
|
|
protected $root;
|
|
|
|
/**
|
|
* @var array Modules to store in manifest.
|
|
*/
|
|
protected $modules = ['system', 'backend', 'cms'];
|
|
|
|
/**
|
|
* @var array Files cache.
|
|
*/
|
|
protected $files = [];
|
|
|
|
/**
|
|
* @var array File extensions to normalize newlines for
|
|
*/
|
|
protected $normalizeExtensions = [
|
|
'css',
|
|
'htm',
|
|
'html',
|
|
'js',
|
|
'json',
|
|
'less',
|
|
'md',
|
|
'php',
|
|
'sass',
|
|
'scss',
|
|
'svg',
|
|
'txt',
|
|
'xml',
|
|
'yaml',
|
|
];
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $root The root folder to get the file list from.
|
|
* @param array $modules An array of modules to include in the file manifest.
|
|
*/
|
|
public function __construct($root = null, array $modules = null)
|
|
{
|
|
if (isset($root)) {
|
|
$this->setRoot($root);
|
|
} else {
|
|
$this->setRoot(base_path());
|
|
}
|
|
|
|
if (isset($modules)) {
|
|
$this->setModules($modules);
|
|
} else {
|
|
$this->setModules(Config::get('cms.loadModules', ['System', 'Backend', 'Cms']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the root folder.
|
|
*
|
|
* @param string $root
|
|
* @throws ApplicationException If the specified root does not exist.
|
|
*/
|
|
public function setRoot($root)
|
|
{
|
|
if (is_string($root)) {
|
|
$this->root = realpath($root);
|
|
|
|
if ($this->root === false || !is_dir($this->root)) {
|
|
throw new ApplicationException(
|
|
'Invalid root specified for the file manifest.'
|
|
);
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets the modules.
|
|
*
|
|
* @param array $modules
|
|
*/
|
|
public function setModules(array $modules)
|
|
{
|
|
$this->modules = array_map(function ($module) {
|
|
return strtolower($module);
|
|
}, $modules);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets a list of files and their corresponding hashsums.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getFiles()
|
|
{
|
|
if (count($this->files)) {
|
|
return $this->files;
|
|
}
|
|
|
|
$files = [];
|
|
|
|
foreach ($this->modules as $module) {
|
|
$path = $this->root . '/modules/' . $module;
|
|
|
|
if (!is_dir($path)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($this->findFiles($path) as $file) {
|
|
$files[$this->getFilename($file)] = hash('sha3-256', $this->normalizeFileContents($file));
|
|
}
|
|
}
|
|
|
|
return $this->files = $files;
|
|
}
|
|
|
|
/**
|
|
* Gets the checksum of a specific install.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getModuleChecksums()
|
|
{
|
|
if (!count($this->files)) {
|
|
$this->getFiles();
|
|
}
|
|
|
|
$modules = [];
|
|
foreach ($this->modules as $module) {
|
|
$modules[$module] = '';
|
|
}
|
|
|
|
foreach ($this->files as $path => $hash) {
|
|
// Determine module
|
|
$module = explode('/', $path)[2];
|
|
|
|
$modules[$module] .= $hash;
|
|
}
|
|
|
|
return array_map(function ($moduleSum) {
|
|
return hash('sha3-256', $moduleSum);
|
|
}, $modules);
|
|
}
|
|
|
|
/**
|
|
* Finds all files within the path.
|
|
*
|
|
* @param string $basePath The base path to look for files within.
|
|
* @return array
|
|
*/
|
|
protected function findFiles(string $basePath)
|
|
{
|
|
$datasource = new FileDatasource($basePath, new Filesystem);
|
|
|
|
$files = array_map(function ($path) use ($basePath) {
|
|
return $basePath . '/' . $path;
|
|
}, array_keys($datasource->getAvailablePaths()));
|
|
|
|
// Ensure files are sorted so they are in a consistent order, no matter the way the OS returns the file list.
|
|
sort($files, SORT_NATURAL);
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Returns the filename without the root.
|
|
*
|
|
* @param string $file
|
|
* @return string
|
|
*/
|
|
protected function getFilename(string $file): string
|
|
{
|
|
return str_replace($this->root, '', $file);
|
|
}
|
|
|
|
/**
|
|
* Normalises the file contents, irrespective of OS.
|
|
*
|
|
* @param string $file
|
|
* @return string
|
|
*/
|
|
protected function normalizeFileContents(string $file): string
|
|
{
|
|
if (!is_file($file)) {
|
|
return '';
|
|
}
|
|
|
|
$contents = file_get_contents($file);
|
|
|
|
// Replace Windows newlines in text files with Unix newlines
|
|
if (
|
|
PHP_EOL === "\r\n"
|
|
&& in_array(pathinfo($file, PATHINFO_EXTENSION), $this->normalizeExtensions)
|
|
) {
|
|
$contents = str_replace(PHP_EOL, "\n", $contents);
|
|
}
|
|
|
|
return $contents;
|
|
}
|
|
}
|