You are here

class LinkToFileConstraint in File Link 8

Same name and namespace in other branches
  1. 2.0.x src/Plugin/Validation/Constraint/LinkToFileConstraint.php \Drupal\file_link\Plugin\Validation\Constraint\LinkToFileConstraint

Validation constraint for file_link, checking that URI points to a file.

Plugin annotation


@Constraint(
  id = "LinkToFile",
  label = @Translation("Checks that URI links to a file.", context = "Validation"),
)

Hierarchy

  • class \Drupal\file_link\Plugin\Validation\Constraint\LinkToFileConstraint extends \Symfony\Component\Validator\Constraint implements \Symfony\Component\Validator\ConstraintValidatorInterface

Expanded class hierarchy of LinkToFileConstraint

File

src/Plugin/Validation/Constraint/LinkToFileConstraint.php, line 24

Namespace

Drupal\file_link\Plugin\Validation\Constraint
View source
class LinkToFileConstraint extends Constraint implements ConstraintValidatorInterface {

  /**
   * Validation execution context.
   *
   * @var \Symfony\Component\Validator\Context\ExecutionContextInterface
   */
  protected $context;

  /**
   * {@inheritdoc}
   */
  public function initialize(ExecutionContextInterface $context) {
    $this->context = $context;
  }

  /**
   * {@inheritdoc}
   */
  public function validatedBy() {
    return get_class($this);
  }

  /**
   * {@inheritdoc}
   */
  public function validate($link, Constraint $constraint) {

    /** @var \Drupal\file_link\Plugin\Field\FieldType\FileLinkItem $link */
    if ($link
      ->isEmpty()) {
      return;
    }
    $is_valid = TRUE;
    $uri = $link
      ->get('uri')
      ->getValue();

    // Try to resolve the given URI to a URL. It may fail if it's schemeless.
    try {
      $url = Url::fromUri($uri, [
        'absolute' => TRUE,
      ])
        ->toString();
    } catch (\InvalidArgumentException $e) {
      $this->context
        ->addViolation("The following error occurred while getting the link URL: @error", [
        '@error' => $e
          ->getMessage(),
      ]);
      $is_valid = FALSE;
    }
    if ($is_valid) {

      // If URL has no path but it still needs an extension then it's not valid.
      if (!$this
        ->hasPath($url) && $this
        ->needsExtension($link)) {
        $this->context
          ->addViolation("Provided file URL has no path nor extension: @uri", [
          '@uri' => $uri,
        ]);
        $is_valid = FALSE;
      }
      if ($is_valid) {

        // Check for redirect response and get effective URL if any.
        $url = $this
          ->getEffectiveUrl($url);
        if ($this
          ->needsExtension($link) && !$this
          ->hasExtension($url)) {
          $this->context
            ->addViolation("Provided file URL has no extension: @uri", [
            '@uri' => $uri,
          ]);
          $is_valid = FALSE;
        }
        if ($is_valid && $this
          ->hasExtension($url)) {
          $is_valid = $this
            ->hasValidExtension($url, $link);
        }
      }
    }

    // If not valid construct error message.
    if (!$is_valid) {
      $this->context
        ->addViolation("Provided file URL has no valid extension: @uri", [
        '@uri' => $uri,
      ]);
    }
  }

  /**
   * Check whereas given URL has a path.
   *
   * @param string $url
   *   URL.
   *
   * @return bool
   *   Whereas given URL has a path.
   */
  protected function hasPath($url) {
    return !empty($this
      ->getPath($url));
  }

  /**
   * Get URL path.
   *
   * @param string $url
   *   URL.
   *
   * @return string
   *   URL path.
   */
  protected function getPath($url) {
    return trim((string) parse_url($url, PHP_URL_PATH), '/');
  }

  /**
   * Check whereas given URL has an extension.
   *
   * @param string $url
   *   URL.
   *
   * @return bool
   *   Whereas given URL has an extension.
   */
  protected function hasExtension($url) {
    return !empty(pathinfo($this
      ->getPath($url), PATHINFO_EXTENSION));
  }

  /**
   * Check whereas given link field needs an extension.
   *
   * @param \Drupal\file_link\Plugin\Field\FieldType\FileLinkItem $link
   *   Link item.
   *
   * @return bool
   *   Whereas link item needs an extension.
   */
  protected function needsExtension(FileLinkItem $link) {
    return !$link
      ->getFieldDefinition()
      ->getSetting('no_extension');
  }

  /**
   * Check whereas basename has a valid extension.
   *
   * @param string $basename
   *   URL path basename.
   * @param \Drupal\file_link\Plugin\Field\FieldType\FileLinkItem $link
   *   Link item.
   *
   * @return bool
   *   Whereas basename has a valid extension.
   */
  protected function hasValidExtension($basename, FileLinkItem $link) {
    $extensions = trim($link
      ->getFieldDefinition()
      ->getSetting('file_extensions'));
    if (!empty($extensions)) {
      $regex = '/\\.(' . preg_replace('/ +/', '|', preg_quote($extensions)) . ')$/i';
      return (bool) preg_match($regex, $basename) !== FALSE;
    }
    return TRUE;
  }

  /**
   * Get effective URL by following redirects, if any.
   *
   * @param string $url
   *   Original URL.
   *
   * @return string
   *   Effective URL.
   */
  protected function getEffectiveUrl($url) {

    // Skip performing HTTP requests, useful when running bulk imports.
    if (Settings::get('file_link.disable_http_requests', FALSE) || !Settings::get('file_link.follow_redirect_on_validate', TRUE)) {
      return $url;
    }

    // Setup HTTP client to follow redirect and perform an HEAD request.
    $options = [
      'exceptions' => TRUE,
      'connect_timeout' => TRUE,
      'allow_redirects' => [
        'strict' => TRUE,
        'on_redirect' => function (Request $request, Response $response, Uri $uri) use (&$url) {
          $url = (string) $uri;
        },
      ],
    ];
    try {

      // Perform HEAD request to get actual URL, as in: after the redirect.
      \Drupal::httpClient()
        ->head($url, $options);
    } catch (RequestException $e) {

      // Don't fail validation if connection has timed out or URL was not found.
    }
    return $url;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
LinkToFileConstraint::$context protected property Validation execution context.
LinkToFileConstraint::getEffectiveUrl protected function Get effective URL by following redirects, if any.
LinkToFileConstraint::getPath protected function Get URL path.
LinkToFileConstraint::hasExtension protected function Check whereas given URL has an extension.
LinkToFileConstraint::hasPath protected function Check whereas given URL has a path.
LinkToFileConstraint::hasValidExtension protected function Check whereas basename has a valid extension.
LinkToFileConstraint::initialize public function Initializes the constraint validator.
LinkToFileConstraint::needsExtension protected function Check whereas given link field needs an extension.
LinkToFileConstraint::validate public function Checks if the passed value is valid.
LinkToFileConstraint::validatedBy public function Returns the name of the class that validates this constraint.