utilikit-1.0.0/src/Service/UtilikitServiceProvider.php
src/Service/UtilikitServiceProvider.php
<?php
declare(strict_types=1);
namespace Drupal\utilikit\Service;
use Drupal\Core\Config\ConfigFactoryInterface;
use Psr\Log\LoggerInterface;
/**
* Main service provider for Utilikit module operations.
*
* This class acts as a facade that coordinates between various Utilikit
* services to provide high-level functionality such as CSS generation,
* file management, content scanning, and switching between rendering modes.
*
* The service provider ensures proper coordination between:
* - CSS generation from utility classes
* - File system operations for static CSS files
* - Content scanning for utility class discovery
* - State management for persistent data
* - Cache management for performance optimization
*/
class UtilikitServiceProvider {
/**
* The CSS generator service.
*
* @var \Drupal\utilikit\Service\UtilikitCssGenerator
*/
protected UtilikitCssGenerator $cssGenerator;
/**
* The file manager service.
*
* @var \Drupal\utilikit\Service\UtilikitFileManager
*/
protected UtilikitFileManager $fileManager;
/**
* The content scanner service.
*
* @var \Drupal\utilikit\Service\UtilikitContentScanner
*/
protected UtilikitContentScanner $contentScanner;
/**
* The state manager service.
*
* @var \Drupal\utilikit\Service\UtilikitStateManager
*/
protected UtilikitStateManager $stateManager;
/**
* The cache manager service.
*
* @var \Drupal\utilikit\Service\UtilikitCacheManagerInterface
*/
protected UtilikitCacheManagerInterface $cacheManager;
/**
* The logger service.
*
* @var \Psr\Log\LoggerInterface
*/
protected LoggerInterface $logger;
/**
* The test CSS generator service.
*
* @var \Drupal\utilikit\Service\UtilikitTestCssGenerator|null
*/
protected ?UtilikitTestCssGenerator $testCssGenerator;
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected ConfigFactoryInterface $configFactory;
/**
* Constructs a new UtilikitServiceProvider.
*
* @param \Drupal\utilikit\Service\UtilikitCssGenerator $cssGenerator
* The CSS generator service for creating CSS from utility classes.
* @param \Drupal\utilikit\Service\UtilikitFileManager $fileManager
* The file manager service for handling static CSS file operations.
* @param \Drupal\utilikit\Service\UtilikitContentScanner $contentScanner
* The content scanner service for discovering utility classes.
* @param \Drupal\utilikit\Service\UtilikitStateManager $stateManager
* The state manager service for persistent data operations.
* @param \Drupal\utilikit\Service\UtilikitCacheManagerInterface $cacheManager
* The cache manager service for cache operations and invalidation.
* @param \Psr\Log\LoggerInterface $logger
* The logger service for recording operations and errors.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The configuration factory service.
* @param \Drupal\utilikit\Service\UtilikitTestCssGenerator|null $testCssGenerator
* The test generator service for creating all possible CSS classes.
*/
public function __construct(
UtilikitCssGenerator $cssGenerator,
UtilikitFileManager $fileManager,
UtilikitContentScanner $contentScanner,
UtilikitStateManager $stateManager,
UtilikitCacheManagerInterface $cacheManager,
LoggerInterface $logger,
ConfigFactoryInterface $configFactory,
?UtilikitTestCssGenerator $testCssGenerator = NULL,
) {
$this->cssGenerator = $cssGenerator;
$this->fileManager = $fileManager;
$this->contentScanner = $contentScanner;
$this->stateManager = $stateManager;
$this->cacheManager = $cacheManager;
$this->logger = $logger;
$this->configFactory = $configFactory;
$this->testCssGenerator = $testCssGenerator;
}
/**
* Gets the CSS generator service.
*
* @return \Drupal\utilikit\Service\UtilikitCssGenerator
* The CSS generator service instance.
*/
public function getCssGenerator(): UtilikitCssGenerator {
return $this->cssGenerator;
}
/**
* Gets the file manager service.
*
* @return \Drupal\utilikit\Service\UtilikitFileManager
* The file manager service instance.
*/
public function getFileManager(): UtilikitFileManager {
return $this->fileManager;
}
/**
* Gets the content scanner service.
*
* @return \Drupal\utilikit\Service\UtilikitContentScanner
* The content scanner service instance.
*/
public function getContentScanner(): UtilikitContentScanner {
return $this->contentScanner;
}
/**
* Gets the state manager service.
*
* @return \Drupal\utilikit\Service\UtilikitStateManager
* The state manager service instance.
*/
public function getStateManager(): UtilikitStateManager {
return $this->stateManager;
}
/**
* Gets the cache manager service.
*
* @return \Drupal\utilikit\Service\UtilikitCacheManagerInterface
* The cache manager service instance.
*/
public function getCacheManager(): UtilikitCacheManagerInterface {
return $this->cacheManager;
}
/**
* Gets the current rendering mode from configuration.
*
* @return string
* The rendering mode: 'inline', 'static', or 'head'. Defaults to 'inline'
* if not configured.
*/
public function getRenderingMode(): string {
$config = $this->configFactory->get('utilikit.settings');
return $config->get('rendering_mode') ?? 'inline';
}
/**
* Updates CSS and static file with new utility classes.
*
* This method validates the provided classes, updates the known classes
* state, generates new CSS, and ensures the static CSS file is current.
* It also invalidates relevant caches to ensure changes are visible.
*
* @param array $classes
* An array of utility class names to process and add to the CSS.
*
* @return bool
* TRUE if the CSS and file were successfully updated, FALSE otherwise.
* Returns FALSE if no classes provided or no valid classes found.
*/
public function updateCssAndFile(array $classes): bool {
if (empty($classes)) {
return FALSE;
}
$validClasses = $this->contentScanner->validateUtilityClasses($classes);
if (empty($validClasses)) {
return FALSE;
}
$allClasses = $this->stateManager->addKnownClasses($validClasses);
$css = $this->cssGenerator->generateCssFromClasses($allClasses);
$this->stateManager->setGeneratedCss($css);
$result = $this->fileManager->ensureStaticCssFile();
if ($result) {
$this->stateManager->updateCssTimestamp();
$this->cacheManager->clearCssCaches();
}
return $result;
}
/**
* Regenerates the static CSS file from existing known classes.
*
* This method uses the currently stored known classes to regenerate
* the CSS and update the static file. Useful for rebuilding CSS
* without scanning content again. Uses conservative cache clearing
* to avoid deleting the newly created CSS file.
*
* @return bool
* TRUE if the CSS was successfully regenerated, FALSE if no known
* classes exist or file operations failed.
*
* @throws \Exception
* Thrown if CSS generation or file operations encounter critical errors.
*/
public function regenerateStaticCss(): bool {
try {
$knownClasses = $this->stateManager->getKnownClasses();
if (empty($knownClasses)) {
$this->logger->info('No known classes found for CSS regeneration.');
return FALSE;
}
$classCount = count($knownClasses);
$this->logger->info('Regenerating CSS with @count known classes.', [
'@count' => $classCount,
]);
// Generate CSS from all known classes.
$css = $this->cssGenerator->generateCssFromClasses($knownClasses);
if (empty($css)) {
$this->logger->warning('CSS generation produced empty output.');
return FALSE;
}
// Store the generated CSS in state.
$this->stateManager->setGeneratedCss($css);
// Ensure the static CSS file is created/updated.
$fileResult = $this->fileManager->ensureStaticCssFile();
if (!$fileResult) {
$this->logger->error('Failed to create static CSS file.');
return FALSE;
}
// Update timestamp and clear caches after successful file creation.
// Use conservative strategy to avoid deleting the file we just created.
$this->stateManager->updateCssTimestamp();
$this->cacheManager->clearCachesWithStrategy(['strategy' => 'conservative']);
$this->logger->info('Static CSS regenerated successfully. Size: @size bytes.', [
'@size' => strlen($css),
]);
return TRUE;
}
catch (\Exception $e) {
$this->logger->error('CSS regeneration failed: @message', [
'@message' => $e->getMessage(),
'@trace' => $e->getTraceAsString(),
]);
// Re-throw for proper error handling upstream.
throw $e;
}
}
/**
* Switches Utilikit to static rendering mode.
*
* This method performs a complete transition to static mode by:
* - Clearing existing Utilikit data
* - Cleaning up old static files
* - Scanning all content for utility classes
* - Generating and saving CSS to a static file
* - Clearing all caches.
*
* @return array
* An associative array containing:
* - 'classes_count': Number of unique utility classes found
* - 'scanned_count': Total number of entities scanned
*/
public function switchToStaticMode(): array {
$this->stateManager->clearUtilikitData();
$this->fileManager->cleanupStaticFiles();
$scanResult = $this->contentScanner->scanAllContent();
$knownClasses = $scanResult['classes'];
$totalScanned = $scanResult['scanned_count'];
if (!empty($knownClasses)) {
$this->stateManager->setKnownClasses($knownClasses);
$css = $this->cssGenerator->generateCssFromClasses($knownClasses);
$this->stateManager->setGeneratedCss($css);
$this->fileManager->ensureStaticCssFile();
}
else {
$this->stateManager->setGeneratedCss('/* Utilikit Static CSS - Add utility classes to your content */');
$this->fileManager->ensureStaticCssFile();
}
$this->stateManager->updateCssTimestamp();
$this->cacheManager->clearAllCaches();
return [
'classes_count' => count($knownClasses),
'scanned_count' => $totalScanned,
];
}
/**
* Gets the test CSS generator service.
*
* @return \Drupal\utilikit\Service\UtilikitTestCssGenerator|null
* The test CSS generator service, or NULL if not available.
*/
public function getTestCssGenerator(): ?UtilikitTestCssGenerator {
return $this->testCssGenerator;
}
/**
* Switches Utilikit to inline rendering mode.
*
* This method transitions from static to inline mode by:
* - Cleaning up static CSS files
* - Updating the CSS timestamp for cache invalidation
* - Clearing all caches.
*
* The known classes are preserved for potential future use.
*
* @return int
* The number of known utility classes that will continue to be
* available in inline mode.
*/
public function switchToInlineMode(): int {
$this->fileManager->cleanupStaticFiles();
$this->stateManager->updateCssTimestamp();
$this->cacheManager->clearAllCaches();
return count($this->stateManager->getKnownClasses());
}
}
