countdown-8.x-1.8/src/Utility/LibraryPathResolver.php
src/Utility/LibraryPathResolver.php
<?php
declare(strict_types=1);
namespace Drupal\countdown\Utility;
use Drupal\Core\Asset\LibrariesDirectoryFileFinder;
use Drupal\Core\DrupalKernel;
use Drupal\Core\File\FileSystemInterface;
use Psr\Log\LoggerInterface;
/**
* Resolves library installation paths.
*
* This utility unifies library path discovery logic previously duplicated
* across CountdownLibraryPluginBase and CountdownLibraryDiscovery.
*/
class LibraryPathResolver {
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected FileSystemInterface $fileSystem;
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected LoggerInterface $logger;
/**
* Whether debug mode is enabled.
*
* @var bool
*/
protected bool $debugMode;
/**
* Cached library paths.
*
* @var array
*/
protected array $pathCache = [];
/**
* Constructs a LibraryPathResolver.
*
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
* @param bool $debug_mode
* Whether debug mode is enabled.
*/
public function __construct(
FileSystemInterface $file_system,
LoggerInterface $logger,
bool $debug_mode = FALSE,
) {
$this->fileSystem = $file_system;
$this->logger = $logger;
$this->debugMode = $debug_mode;
}
/**
* Finds a library installation path.
*
* @param string $library_id
* The library identifier.
* @param array $possible_names
* Array of possible folder names to search.
*
* @return string|null
* The library path relative to DRUPAL_ROOT, or NULL if not found.
*/
public function findLibrary(string $library_id, array $possible_names): ?string {
// Check cache first.
$cache_key = $library_id . ':' . implode(',', $possible_names);
if (isset($this->pathCache[$cache_key])) {
return $this->pathCache[$cache_key];
}
// Always include the library ID itself.
if (!in_array($library_id, $possible_names)) {
array_unshift($possible_names, $library_id);
}
foreach ($possible_names as $name) {
$path = $this->searchLibraryPath($name);
if ($path !== NULL) {
// Ensure path starts with /.
$path = '/' . ltrim($path, '/');
$this->pathCache[$cache_key] = $path;
if ($this->debugMode) {
$this->logger->debug('Found library @library at path: @path', [
'@library' => $library_id,
'@path' => $path,
]);
}
return $path;
}
}
$this->pathCache[$cache_key] = NULL;
return NULL;
}
/**
* Searches for a library by folder name.
*
* @param string $library_name
* The library folder name to search for.
*
* @return string|null
* The library path or NULL if not found.
*/
protected function searchLibraryPath(string $library_name): ?string {
$request = \Drupal::request();
if (\Drupal::hasService('kernel')) {
$site_path = \Drupal::getContainer()->getParameter('site.path');
}
else {
$site_path = DrupalKernel::findSitePath($request);
}
$root = DRUPAL_ROOT;
$profile_extension_list = \Drupal::service('extension.list.profile');
$install_profile = \Drupal::installProfile();
$libraries_finder = new LibrariesDirectoryFileFinder(
$root,
$site_path,
$profile_extension_list,
$install_profile
);
$result = $libraries_finder->find($library_name);
if ($result !== FALSE) {
if ($this->debugMode) {
$this->logger->debug('Library @name found at: @path', [
'@name' => $library_name,
'@path' => $result,
]);
}
return $result;
}
if ($this->debugMode) {
$this->logger->debug('Library @name not found in any search path.', [
'@name' => $library_name,
]);
}
return NULL;
}
/**
* Validates that a library is installed at the given path.
*
* @param string $path
* The path to validate (relative to DRUPAL_ROOT).
* @param array $required_files
* Array of required file paths relative to library root.
* @param array $alternative_paths
* Array of alternative path sets.
*
* @return bool
* TRUE if the library is valid at this path, FALSE otherwise.
*/
public function validateInstallation(string $path, array $required_files, array $alternative_paths = []): bool {
$path = ltrim($path, '/');
$full_path = DRUPAL_ROOT . '/' . $path;
if (!is_dir($full_path)) {
if ($this->debugMode) {
$this->logger->debug('Library directory does not exist: @path', [
'@path' => $full_path,
]);
}
return FALSE;
}
// If no required files, consider it valid.
if (empty($required_files)) {
return TRUE;
}
// Check primary required files.
$all_found = TRUE;
$missing_files = [];
foreach ($required_files as $file) {
$file_path = $full_path . '/' . $file;
if (!file_exists($file_path)) {
$all_found = FALSE;
$missing_files[] = $file;
}
}
if ($all_found) {
return TRUE;
}
// Check alternative paths.
foreach ($alternative_paths as $alt_set) {
$alt_found = TRUE;
foreach ($alt_set as $alt_file) {
$file_path = $full_path . '/' . $alt_file;
if (!file_exists($file_path)) {
$alt_found = FALSE;
break;
}
}
if ($alt_found) {
return TRUE;
}
}
if ($this->debugMode) {
$this->logger->debug('Library validation failed. Missing files: @files', [
'@files' => implode(', ', $missing_files),
]);
}
return FALSE;
}
/**
* Clears the internal path cache.
*/
public function clearCache(): void {
$this->pathCache = [];
}
}
