maintenance-1.0.0-beta1/src/MaintenanceManager.php

src/MaintenanceManager.php
<?php

namespace Drupal\maintenance;

use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Provides the default implementation of the maintenance manager service.
 */
class MaintenanceManager implements MaintenanceManagerInterface {

  use StringTranslationTrait;
  use MaintenanceStringHelperTrait;

  /**
   * The configuration factory service.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected ConfigFactoryInterface $config;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected AccountInterface $account;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected ModuleHandlerInterface $moduleHandler;

  /**
   * Constructs a new MaintenanceManager instance.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory service.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user object.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   */
  public function __construct(ConfigFactoryInterface $config_factory, AccountInterface $account, ModuleHandlerInterface $module_handler) {
    $this->config = $config_factory;
    $this->account = $account;
    $this->moduleHandler = $module_handler;
  }

  /**
   * {@inheritdoc}
   */
  public function getConfig(): ImmutableConfig|Config {
    return $this->config->getEditable('maintenance.settings');
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultDateFormat(): string {
    // Could be moved to settings later.
    return 'Y-m-d H:i:s';
  }

  /**
   * {@inheritdoc}
   */
  public function getAllowedUnits(): array {
    return [
      'days' => $this->t('day(s)'),
      'hours' => $this->t('hour(s)'),
      'minutes' => $this->t('minute(s)'),
      'seconds' => $this->t('second(s)'),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getAllowedIps(): array {
    // Retrieve the list of allowed IP addresses.
    $allowed_ips = $this->getConfig()->get('ip.addresses') ?? '';

    // Normalize to string if stored as array.
    if (is_array($allowed_ips)) {
      $allowed_ips = $this->arrayToString($allowed_ips);
    }

    // Cast non-string input safely.
    $ips = is_string($allowed_ips) ? $allowed_ips : '';

    // Safely split into trimmed, non-empty lines.
    $lines = preg_split('/\r?\n/', $ips) ?: [];

    // Return filtered list with trimmed, non-empty or invalid entries.
    return array_values(array_filter(array_map('trim', $lines), static function ($ip) {
      return $ip !== '';
    }));
  }

  /**
   * {@inheritdoc}
   */
  public function getAllowedUrls(): array {
    // Retrieve the list of allowed route paths.
    $allowed_urls = $this->getConfig()->get('page.routes') ?? '';

    // Normalize to string if stored as array.
    if (is_array($allowed_urls)) {
      $allowed_urls = $this->arrayToString($allowed_urls);
    }

    // Ensure it's a string before splitting.
    $urls = is_string($allowed_urls) ? $allowed_urls : '';

    // Safely split into trimmed, non-empty lines.
    $lines = preg_split('/\r?\n/', $urls) ?: [];

    // Return filtered list with trimmed, non-empty or invalid entries.
    return array_values(array_filter(array_map('trim', $lines), static function ($url) {
      return $url !== '';
    }));
  }

  /**
   * {@inheritdoc}
   */
  public function getAvailableThemes(array $options = []): array {
    // Get all available theme options via hook_maintenance_page_theme().
    // Array reversed to prioritize module-defined templates.
    $theme_info = array_reverse(
      $this->moduleHandler->invokeAll('maintenance_page_theme', [$options])
    );

    // Default options provided by the module.
    $theme_options = [
      '' => $this->t('Default theme'),
      'clean' => $this->t('Clean'),
    ];

    foreach ($theme_info as $theme => $info) {
      // Allow only lowercase alphabetic machine names.
      if (!preg_match('/^[a-z]+$/', $theme)) {
        continue;
      }

      // Validate presence of required metadata.
      $name = trim($info['name'] ?? '');
      $description = trim($info['description'] ?? '');
      if ($name === '' || $description === '') {
        continue;
      }

      // Add the valid option to the list.
      $theme_options[$theme] = [
        'name' => $name,
        'description' => $description,
      ];
    }

    return $theme_options;
  }

  /**
   * {@inheritdoc}
   */
  public function getHttpStatusCodes(): array {
    return [
      // Informational 1xx.
      100 => 'Continue',
      101 => 'Switching Protocols',
      102 => 'Processing',

      // Success 2xx.
      200 => 'OK',
      201 => 'Created',
      202 => 'Accepted',
      203 => 'Non-Authoritative Information',
      204 => 'No Content',
      205 => 'Reset Content',
      206 => 'Partial Content',
      207 => 'Multi-Status',

      // Redirection 3xx.
      300 => 'Multiple Choices',
      301 => 'Moved Permanently',
      302 => 'Found',
      303 => 'See Other',
      304 => 'Not Modified',
      305 => 'Use Proxy',
      306 => 'Switch Proxy',
      307 => 'Temporary Redirect',

      // Client Errors 4xx.
      400 => 'Bad Request',
      401 => 'Unauthorized',
      402 => 'Payment Required',
      403 => 'Forbidden',
      404 => 'Not Found',
      405 => 'Method Not Allowed',
      406 => 'Not Acceptable',
      407 => 'Proxy Authentication Required',
      408 => 'Request Timeout',
      409 => 'Conflict',
      410 => 'Gone',
      411 => 'Length Required',
      412 => 'Precondition Failed',
      413 => 'Request Entity Too Large',
      414 => 'Request-URI Too Long',
      415 => 'Unsupported Media Type',
      416 => 'Requested Range Not Satisfiable',
      417 => 'Expectation Failed',
      418 => 'I\'m a teapot',
      422 => 'Unprocessable Entity',
      423 => 'Locked',
      424 => 'Failed Dependency',
      425 => 'Unordered Collection',
      426 => 'Upgrade Required',
      449 => 'Retry With',
      450 => 'Blocked by Windows Parental Controls',

      // Server Errors 5xx.
      500 => 'Internal Server Error',
      501 => 'Not Implemented',
      502 => 'Bad Gateway',
      503 => 'Service Unavailable',
      504 => 'Gateway Timeout',
      505 => 'HTTP Version Not Supported',
      506 => 'Variant Also Negotiates',
      507 => 'Insufficient Storage',
      509 => 'Bandwidth Limit Exceeded',
      510 => 'Not Extended',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getHttpStatusLabel(int $code): ?string {
    $codes = $this->getHttpStatusCodes();
    return $codes[$code] ?? NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function hasAccess(): bool {
    return $this->account->hasPermission('access site in maintenance mode');
  }

  /**
   * {@inheritdoc}
   */
  public function isCidrMatch(string $ip): bool {
    // Load all IP addresses allowed during maintenance mode.
    $allowed_ips = $this->getAllowedIps();

    // Loop through each entry to check for CIDR match.
    foreach ($allowed_ips as $entry) {
      // Only evaluate entries in CIDR format (ignore plain IPs).
      // Return TRUE if the IP matches the current CIDR block.
      if (strpos($entry, '/') !== FALSE && $this->checkCidrMatch($ip, $entry)) {
        return TRUE;
      }
    }

    // No matching CIDR found.
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function checkCidrMatch(string $ip, string $cidr): bool {
    // Check if the CIDR string contains a '/' separator.
    if (strpos($cidr, '/') === FALSE) {
      return FALSE;
    }

    // Split the CIDR into network address and mask length.
    [$network, $maskLength] = explode('/', $cidr, 2);

    // Convert the network address to a 32-bit integer.
    $networkLong = ip2long($network);
    if ($networkLong === FALSE) {
      return FALSE;
    }

    // Convert the input IP address to a 32-bit integer.
    $ipLong = ip2long($ip);
    if ($ipLong === FALSE) {
      return FALSE;
    }

    // Cast mask length to integer and validate its range (0-32).
    $maskLength = (int) $maskLength;
    if ($maskLength < 0 || $maskLength > 32) {
      return FALSE;
    }

    // Generate the subnet mask using bitwise operations.
    $subnetMask = ~((1 << (32 - $maskLength)) - 1);

    // Compare the masked IP and network addresses.
    return ($ipLong & $subnetMask) === ($networkLong & $subnetMask);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc