File: D:/HostingSpaces/brameda/brameda.nl/app/Komma/Shop/TranslationInstallationController.php
<?php
namespace App\Komma\Shop;
use Illuminate\Console\Command;
/**
* Class InstallationController
* @package App\Komma\Shop
*/
class TranslationInstallationController
{
/**
* @var $translationsFolder string A folder where locale folders (ex: en, nl as iso2) are located wherein translations files can be found
*/
private $translationsFolder;
/**
* @var $laravelsTranslationsFolder string The folder to the lang resource folder. Usually this is resources/lang
*/
private $laravelsTranslationsFolder;
public function __construct()
{
$this->translationsFolder = __DIR__.DIRECTORY_SEPARATOR.'Translations'.DIRECTORY_SEPARATOR;
$this->laravelsTranslationsFolder = resource_path('lang');
$this->laravelsTranslationsFolder = $this->laravelsTranslationsFolder.DIRECTORY_SEPARATOR;
}
/**
* Installs the shop translations to resources\lang folders depending on the folders in the shops Translations folder
* @param Command $command
* @param bool $installViaCopy
*/
public function translations(Command $command, $installViaCopy = false)
{
$folders = $this->getShopTranslationFolders();
$command->info("Detected the following shop translation folders: ".implode(", ", $folders));
//Create the folder in Laravel's lang resource folder if it does not exists
$this->createLanguageFoldersForLaravel($folders, $command);
//Get the files from the iso2 folders and create files that include them in Laravel's language resource directory (Pretty much like symbolic linking)
$installation = $this->getInstallation($folders, $command);
$command->info("Installing the shop translation files...");
//Install the translations
foreach($installation as $relativeFile)
{
$dirnameToCheck = dirname($this->laravelsTranslationsFolder.$relativeFile);
if(file_exists($dirnameToCheck) === false) {
mkdir($dirnameToCheck, 0777, true);
}
if($installViaCopy) {
//Install by copy
if (!copy($this->translationsFolder . $relativeFile,
$this->laravelsTranslationsFolder . $relativeFile)) {
throw new \RuntimeException("Could not install language file '" . $relativeFile . "' to '" . $this->laravelsTranslationsFolder . $relativeFile . "'. Check directory permissions first");
}
} else {
//Install by requiring
file_put_contents($this->laravelsTranslationsFolder . $relativeFile, [
'<?php' . PHP_EOL,
'return require("' . $this->translationsFolder . $relativeFile . '");' . PHP_EOL . PHP_EOL
]);
}
}
$command->info("Installed the shop translation files!");
}
/**
* Builds and returns an array that represents the installation that needs to be done or undone.
* For example if you give it the folder Translations (next to this file) it will return for example an array like this:
* ["en/shop/shop.sidebar.php", "nl/shop/shop.sidebar.php"]
*
* @param array $shopTranslationFolders An array containing folder names from the shop Translations folder.
* @param Command $command The artisan command. Used for logging
* @return array
*/
public function getInstallation(array $shopTranslationFolders, Command $command): array
{
$translationFilesCount = 0;
$isoFolderCount = 0;
$destinationsAndSourceFiles = []; //The keys are the destination folders. Values are numeric arrays that contain absolute paths to files that will be put in the destination folders
foreach($shopTranslationFolders as $iso2Folder)
{
// $destinationsAndSourceFiles[$this->laravelsTranslationsFolder.DIRECTORY_SEPARATOR.$iso2Folder] = [];
$translationFoldersAndFiles = $this->scanDirRecursivelyAndReturnPhpFiles(__DIR__.DIRECTORY_SEPARATOR.'Translations'.DIRECTORY_SEPARATOR.$iso2Folder);
foreach($translationFoldersAndFiles as $translationFoldersAndFile)
{
//Skip if folder is the current folder (.) or the parent folder (..)
if($translationFoldersAndFile == '.' || $translationFoldersAndFile == '..') continue;
if(!is_dir($this->translationsFolder.DIRECTORY_SEPARATOR.$translationFoldersAndFile)) {
// $destinationsAndSourceFiles[$this->laravelsTranslationsFolder.DIRECTORY_SEPARATOR.$iso2Folder][] = $translationFoldersAndFile;
$destinationsAndSourceFiles[] = $iso2Folder.DIRECTORY_SEPARATOR.$translationFoldersAndFile;
$translationFilesCount++;
}
}
$isoFolderCount++;
}
if($command) $command->info("Detected ".$translationFilesCount." shop translation file(s) in ".$isoFolderCount." shop translation language (iso2) folder(s)");
return $destinationsAndSourceFiles;
}
/**
* Returns all file paths that are below a certain directory. Relative to the root directory
*
* @param $root string The directory in which we start to search recursively for files
* @param $startRoot string The root directory that the search was started in. Internally used. Must not be used by humans
* @param array $files internal carrier variable. Humans must not use this variable
* @return array
*/
private function scanDirRecursivelyAndReturnPhpFiles($root, $startRoot = null, &$files = []):array
{
if($startRoot == null) $startRoot = $root;
if (is_file($root) || !is_dir($root)) {
if(is_file($root) && pathinfo($root, PATHINFO_EXTENSION) == 'php')
$files[] = str_replace($startRoot.DIRECTORY_SEPARATOR, '', $root);
return $files;
}
// starts the scan
$dirsAndFiles = scandir($root);
foreach ($dirsAndFiles as $dirOrFile) {
if ($dirOrFile == '.' || $dirOrFile == '..') {
continue; // skip . and ..
}
$path = $root . '/' . $dirOrFile;
$this->scanDirRecursivelyAndReturnPhpFiles($path, $startRoot, $files);
}
return $files;
}
/**
* Returns an array of strings containing the names of the folders in the shop's translations folder
*
* @return string[]
*/
private function getShopTranslationFolders(): array
{
$translationFoldersAndFiles = scandir(__DIR__.DIRECTORY_SEPARATOR.'Translations'.DIRECTORY_SEPARATOR, SCANDIR_SORT_ASCENDING);
$folders = [];
foreach($translationFoldersAndFiles as $translationFoldersAndFile)
{
//Skip if folder is the current folder (.) or the parent folder (..)
if($translationFoldersAndFile == '.' || $translationFoldersAndFile == '..') continue;
//Add
if(is_dir($this->translationsFolder.DIRECTORY_SEPARATOR.$translationFoldersAndFile)) {
$folders[] = $translationFoldersAndFile;
}
}
return $folders;
}
/**
* Receives an array containing names of folders that should exist in laravels lang resource folder.
*
* @param array $languageFolderNames
* @param Command $command The artisan command. Used to log progress
*/
private function createLanguageFoldersForLaravel(array $languageFolderNames, Command $command = null): void
{
foreach($languageFolderNames as $iso2Folder)
{
$currentIso2ResourceFolder = $this->laravelsTranslationsFolder.DIRECTORY_SEPARATOR.$iso2Folder;
if(!file_exists($currentIso2ResourceFolder))
if(!mkdir($currentIso2ResourceFolder, 0777, true)) {
throw new \RuntimeException("The iso2 language resource folder '".$currentIso2ResourceFolder."' could not be created and therefore the '".$iso2Folder."' shop translation could not be installed");
} else {
if($command) $command->info("Shop language folder '".$currentIso2ResourceFolder."' created in laravels lang resource folder because it did not exist: '".$this->laravelsTranslationsFolder."'");
};
}
}
/**
* Uninstalls shop translations
* @param Command $command
*/
public function translationsUninstall(Command $command)
{
$folders = $this->getShopTranslationFolders();
$command->info("Detected the following shop translation folders: ".implode(", ", $folders));
//Create the folder in Laravel's lang resource folder if it does not exists
$this->createLanguageFoldersForLaravel($folders, $command);
//Get the files from the iso2 folders and delete files that include them in Laravel's language resource directory (Pretty much like symbolic linking)
$installation = $this->getInstallation($folders, $command);
$command->info("Deleting the shop translation files...");
//delete the translations
foreach($installation as $relativeFile)
{
if(file_exists($this->laravelsTranslationsFolder.$relativeFile) && !unlink($this->laravelsTranslationsFolder.$relativeFile))
throw new \RuntimeException("Installation (partially) aborted. Could not uninstall translation file: ".$this->laravelsTranslationsFolder.$relativeFile);
}
foreach ($installation as $relativeFile)
{
$dir = dirname($this->laravelsTranslationsFolder.$relativeFile);
$this->forceRmDir($dir);
if(file_exists($dir))
throw new \RuntimeException("Installation (partially) aborted. Could not delete directory: ".$dir.'. Try to delete it yourself and uninstall again.');
}
$command->info("Deleted the shop translation files!");
}
/**
* Removes the specifies directory after deleting all items in it
*
* @param $dir
*/
function forceRmDir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") $this->forceRmDir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
rmdir($dir);
}
}
}