Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7df8349
chore: add new classes
RobinTheHood Jun 22, 2023
1dfab48
feat: move Archive classes to Archive and add create() methods
RobinTheHood Jun 22, 2023
2552a38
refactor: use new Archiv in ModuleInstaller.php
RobinTheHood Jun 22, 2023
c8976f7
docs: improve docblock comment
RobinTheHood Jun 22, 2023
777794c
docs: improve docblock comments
RobinTheHood Jun 27, 2023
87b8995
refactor: use . '/' . instead of DIRECTORY_SEPARATOR
RobinTheHood Jun 27, 2023
7902dee
docs: improve docblock comments
RobinTheHood Jun 27, 2023
e546153
fix: do not allow dot in archive and module name
RobinTheHood Jun 27, 2023
aa554d2
test: add ArchiveNameTest
RobinTheHood Jun 27, 2023
7fcce03
feat: add setModulesRootPath() to LocalModuleLoader
RobinTheHood Jun 27, 2023
4c5ef07
fix: path building error
RobinTheHood Jun 27, 2023
c032930
fix: use of unset $_SERVER vars
RobinTheHood Jun 27, 2023
c554d2d
test: add ArchiveHandlerTest and ArchiveTest
RobinTheHood Jun 27, 2023
25330c7
test: add ArchivePullerTest
RobinTheHood Jun 27, 2023
91d9932
Merge branch 'master' into refactor/new-archive-class
RobinTheHood Jun 29, 2023
dcff4be
test: improve test for new Curl HttpRequest
RobinTheHood Jun 29, 2023
7282058
test: some debug messages
RobinTheHood Jun 29, 2023
7d10a0e
test: remove debug message
RobinTheHood Jun 29, 2023
1fbbb59
Merge branch 'master' into refactor/new-archive-class
RobinTheHood Jul 4, 2023
f7325d7
fix: test of ArchiveHandlerTest
RobinTheHood Jul 4, 2023
e9a5cbb
refactor: remove unused code
RobinTheHood Jul 5, 2023
6cb801f
docs: improve code documentation
RobinTheHood Jul 5, 2023
e2fcd0d
refactor: rename method createArchive()
RobinTheHood Jul 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Classes/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,17 @@ public static function getConfigRoot(): string
return self::getRoot() . '/' . self::$configDir;
}

/**
* <MMLC-ROOT>/Archives/
*/
public static function getArchivesRoot(): string
{
return self::getRoot() . '/' . self::$archivesDir;
}

/**
* <MMLC-ROOT>/Modules/
*/
public static function getModulesRoot(): string
{
return self::getRoot() . '/' . self::getModulesDirName();
Expand Down
83 changes: 83 additions & 0 deletions src/Classes/Archive/Archive.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace RobinTheHood\ModifiedModuleLoaderClient\Archive;

use RobinTheHood\ModifiedModuleLoaderClient\Semver\Parser;
use RobinTheHood\ModifiedModuleLoaderClient\Semver\Version;

/**
* Diese Klasse repräsentiert ein Modul gepackt als .tar Datei. Die .tar Datei befindet sich im Datei System und nicht
* als Data-String einem Objekt dieser Klasse. Die Methode getFilePath() liefert die Pfad zu .tar Datei.
*/
class Archive
{
private ArchiveName $archiveName;
private Version $version;
private string $archivesRootPath;

public static function create(string $archiveName, string $version, string $archivesRootPath): Archive
{
$archiveNameObj = new ArchiveName($archiveName);
$semverParser = Parser::create();
$versionObj = $semverParser->parse($version);

return new Archive($archiveNameObj, $versionObj, $archivesRootPath);
}

public function __construct(
ArchiveName $archiveName,
Version $version,
string $archivesRootPath
) {
if (empty($archivesRootPath)) {
throw new \InvalidArgumentException('archivesRootPath cannot be empty.');
}

$this->archiveName = $archiveName;
$this->archivesRootPath = $archivesRootPath;
$this->version = $version;
}

public function getArchiveName(): ArchiveName
{
return $this->archiveName;
}

public function getVersion(): Version
{
return $this->version;
}

/**
* Liefert den Root Path zum Archive Ordner
* z. B. /.../ModifiedModuleLoaderClient/Archives/
*/
public function getArchivesRootPath(): string
{
return $this->archivesRootPath;
}

/**
* Liefert den Dateinamen der .tar Datei
* z. B. robinthehood_modified-std-module_0.1.0.tar
*/
public function getFileName(): string
{
return
str_replace('/', '_', $this->getArchiveName()->__toString())
. '_'
. $this->getVersion()
. '.tar';
}

/**
* Liefert den gesamten Path der .tar Datei.
* z. B. /.../ModifiedModuleLoaderClient/Archives/robinthehood_modified-std-module_0.1.0.tar
*/
public function getFilePath(): string
{
return $this->getArchivesRootPath() . '/' . $this->getFileName();
}
}
151 changes: 151 additions & 0 deletions src/Classes/Archive/ArchiveHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

declare(strict_types=1);

namespace RobinTheHood\ModifiedModuleLoaderClient\Archive;

use RobinTheHood\ModifiedModuleLoaderClient\App;
use RobinTheHood\ModifiedModuleLoaderClient\Helpers\FileHelper;
use RobinTheHood\ModifiedModuleLoaderClient\Loader\LocalModuleLoader;
use RobinTheHood\ModifiedModuleLoaderClient\Module;

/**
* Diese Klasse ist für das Packen und Entpacken von Archiven zuständig
*/
class ArchiveHandler
{
private LocalModuleLoader $localModuleLoader;

/** @var string /.../ModifiedModuleLoaderClient/Modules */
private string $modulesRootPath;

public static function create(int $mode): ArchiveHandler
{
$localModuleLoader = LocalModuleLoader::create($mode);
$archiveHandler = new ArchiveHandler(
$localModuleLoader,
App::getModulesRoot()
);
return $archiveHandler;
}

public function __construct(LocalModuleLoader $localModuleLoader, string $modulesRootPath)
{
$this->localModuleLoader = $localModuleLoader;
$this->modulesRootPath = $modulesRootPath;
}

/**
* Packt ein Archive zu einer .tar Datei.
*
* Dabei ist in der .tar Datei die Verzeichnis-Struktur <vendor-name>/<module-name>/<version> enthalten
*
* @throws \RuntimeException if an error occurs
*/
public function pack(Archive $archive): void
{
$module = $this->localModuleLoader->loadByArchiveNameAndVersion(
(string) $archive->getArchiveName(),
(string) $archive->getVersion()
);

if (!$module) {
throw new \RuntimeException("Failed to load module for archive: " . $archive->getArchiveName());
}

$this->createDirIfNotExists($archive->getArchivesRootPath());
$this->deleteFileIfExists($archive->getFilePath());

$filePaths = FileHelper::scanDirRecursive(
$this->getModulePathFromModule($module),
FileHelper::FILES_ONLY
);

set_time_limit(60 * 10);
$tarArchive = new \PharData($archive->getFilePath());
foreach ($filePaths as $filePath) {
if (file_exists($filePath)) {
$tarPath = FileHelper::stripBasePath($this->modulesRootPath, $filePath);
$tarArchive->addFile($filePath, $tarPath);
}
}
}

/**
* Entpack ein $archive.
*
* Dabei wird das Archive in Modules entpackt. Im .tar archive selbst liegen die Dateien in der Ordner-Strucktur
* /<vendor-name>/<module-name>/<version> vor.
*
* @param Archive $archive
* @param bool $external Wenn eine .tar Datei z. B. von Github kommt, ist die Ordner-Strucktur in der .tar Datei
* nicht kompatible. Im der .tar Datei fehlt das Verzeichnis /<vendor-name>/<module-name>/<version>. Es ist nur
* das Verzeichnis <version> vorhandnen. In diesem Fall muss das Verzeichnis /<vendor-name>/<module-name>/
* angelegt werden.
*
* @throws \RuntimeException if an error occurs
*/
public function extract(Archive $archive, bool $external = false): void
{
$modulePath = $this->getModulePathFromArchive($archive);
if (file_exists($modulePath)) {
throw new \RuntimeException("Module already exists for archive: " . $archive->getArchiveName());
}

$this->createDirIfNotExists($this->modulesRootPath);

$tarArchive = new \PharData($archive->getFilePath());
$tarArchive->extractTo($this->modulesRootPath);

if ($external) {
$vendorDirPath = $this->modulesRootPath . '/' . $archive->getArchiveName()->getVendorName();
$moduleDirPath = $this->modulesRootPath . '/' . $archive->getArchiveName();

$this->createDirIfNotExists($vendorDirPath);
$this->createDirIfNotExists($moduleDirPath);

rename(
$this->modulesRootPath . '/' . $tarArchive->getFileName(),
$this->getModulePathFromArchive($archive)
);
}
}

/**
* Liefert zu einem Archive den ModulePath
* z. B. /.../ModifiedModuleLoaderClient/Modules/composer/autoload/1.0.0/
*/
private function getModulePathFromArchive(Archive $archive): string
{
return $this->modulesRootPath . '/' . $archive->getArchiveName() . '/' . $archive->getVersion();
}

/**
* Liefert den gesatem Modul Path zu einem Modul
* z. B. /.../ModifiedModuleLoaderClient/Modules/composer/autoload/
*/
private function getModulePathFromModule(Module $module): string
{
return $module->getLocalRootPath() . $module->getModulePath();
}

/**
* // TODO: Man könnte diese Methode in die Klasse FileHelper auslagern
*/
private function createDirIfNotExists(string $path): void
{
if (!@mkdir($path) && !is_dir($path)) {
throw new \RuntimeException("Failed to create directory: " . $path);
}
}

/**
* // TODO: Man könnte diese Methode in die Klasse FileHelper auslagern
*/
private function deleteFileIfExists(string $path): void
{
if (!@unlink($path) && file_exists($path)) {
throw new \RuntimeException("Failed to delete file: " . $path);
}
}
}
65 changes: 65 additions & 0 deletions src/Classes/Archive/ArchiveName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of MMLC - ModifiedModuleLoaderClient.
*
* (c) Robin Wieschendorf <mail@robinwieschendorf.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace RobinTheHood\ModifiedModuleLoaderClient\Archive;

/**
* Eine VO (Value Object) Klasse
*
* Da der Name eines Archives nicht willkührlich gewählt sein darf, wir der Name eines Archives durch diese Klasse
* repräsentiert. Der Konstruktor kann beim Erstellen eines Objekts zudem überprüfen, ob es sich um einen validen
* ArchiveNamen-String handelt.
*/
class ArchiveName
{
private string $value;

private string $vendorName;

private string $moduleName;

public function __construct(string $value)
{
if (!$this->isValidArchiveName($value)) {
throw new \InvalidArgumentException('No valid ArchiveName');
}

$this->value = $value;

$parts = explode('/', $value);
$this->vendorName = $parts[0];
$this->moduleName = $parts[1];
}

public function getModuleName(): string
{
return $this->moduleName;
}

public function getVendorName(): string
{
return $this->vendorName;
}

public function __toString(): string
{
return $this->value;
}

private function isValidArchiveName(string $archiveName): bool
{
$pattern = '/^([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)$/';

return preg_match($pattern, $archiveName) === 1;
}
}
Loading