monster_menus-9.0.x-dev/src/PathProcessor/InboundPathProcessor.php

src/PathProcessor/InboundPathProcessor.php
<?php
namespace Drupal\monster_menus\PathProcessor;

use Drupal\Core\Database\Database;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Url;
use Drupal\monster_menus\Constants;
use Drupal\monster_menus\MMOargsStringable;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;

/**
 * Defines a path processor to rewrite URLs in the MM tree.
 *
 * As the route system does not allow an arbitrary number of parameters, convert
 * the path to a set of query parameters on the request.
 */
class InboundPathProcessor implements InboundPathProcessorInterface {

  final public const OARGS_KEY = '_oargs';
  protected $cache, $cache_oargs, $recursive;

  /**
   * {@inheritdoc}
   */
  public function processInbound($path, Request $request) {
    if ($this->recursive) {
      return $path;
    }
    // Redirect "mm/1234.". This is annoyingly common because people create
    // links that include the period in a sentence.
    if (preg_match('{^/(?:mm/)?(-?\d+)\.\s*$}', $path, $path_matches)) {
      return Url::fromRoute('monster_menus.redirect', ['mmtid' => $path_matches[1]], ['base_url' => ''])
        ->toString();
    }
    $original = $path;
    if (!($path = $this->processInboundPath($path, $request->getPathInfo(), TRUE, $oargs))) {
      return $original;
    }
    // If the new path looks like /mm/MMTID ...
    if (preg_match('{^/mm/(-?\d+)(?:/|$)}', $path, $path_matches)) {
      // ... and it's a pseudo-MMTID (integer < 0), show an alpha list of users.
      if ($path_matches[1] < 0) {
        return Url::fromRoute('monster_menus.userlist', ['mmtid' => $path_matches[1]], ['base_url' => ''])
          ->toString();
      }

      if (!$request->query->has(self::OARGS_KEY)) {
        $request->query->set(self::OARGS_KEY, new MMOargsStringable([]));
      }
      $orig_is_mm = preg_match('{^/(mm/)?(-?\d+)(?:/|$)}', $original, $original_matches);
      // ... and there are arguments after the MMTID that do not resolve to a
      // valid path, then move the args into the query.
      if ($oargs) {
        $this->recursive = TRUE;
        try {
          \Drupal::service('router.no_access_checks')->match($path);
        }
        catch (ResourceNotFoundException|ParamNotConvertedException) {
          $parts = explode('/', $path);
          $path = implode('/', array_slice($parts, 0, count($parts) - count($oargs)));
          $request->query->set(self::OARGS_KEY, new MMOargsStringable($oargs));
        }
        $this->recursive = FALSE;
      }
      // ... and the original path looks like /MMTID or /mm/MMTID, and the MMTID
      // does not refer to a page without an alias, do a redirect to the full
      // path so it gets set in the browser. Testing $request->getScriptName()
      // verifies that the request originated from a client and was not
      // internally generated.
      else if (!$oargs && $orig_is_mm && $original_matches[2] == $path_matches[1] && $request->getScriptName() && ($tree = mm_content_get($path_matches[1])) && ($tree->alias || $tree->parent != 1 && $tree->parent != mm_home_mmtid())) {
        return Url::fromRoute('monster_menus.redirect', ['mmtid' => $path_matches[1]], ['base_url' => ''])
          ->toString();
      }
    }
    return $path;
  }

  public function processInboundPath($path, $original_path, $assume_home = TRUE, &$oargs = []) {
    if (preg_match('/^\w+:/', $original_path)) {
      // Ignore URLs starting with "proto:".
      return $path;
    }

    // remove empty '//' elements
    $original_path = preg_replace('{/+}', '/', $original_path);
    $original_path = trim($original_path, '/');
    $path = $original_path;

    if (!$this->cache) {
      $home_path = mm_home_path();
      $this->cache[''] = $this->cache[$home_path] = "/$home_path";
      $this->cache['feed'] = "/$home_path/feed";  // top-level /feed
      $this->cache_oargs[''] = $this->cache_oargs[$home_path] = $this->cache_oargs['feed'] = [];
    }

    if (isset($this->cache[$original_path])) {
      $oargs = $this->cache_oargs[$original_path];
      return $this->cache[$original_path];
    }

    $elems = explode('/', $original_path);
    $in_mm = FALSE;
    if (isset($this->cache[$elems[0]]) && $this->cache[$elems[0]] === FALSE) {
      // This top level element was previously proven to not be in MM.
      $oargs = [];
      return '/' . $original_path;
    }

    for ($i = count($elems); $i > 0; $i--) {
      $temp_path = implode('/', array_slice($elems, 0, $i));
      if (isset($this->cache[$temp_path])) {
        if ($this->cache[$temp_path] === FALSE) {
          $oargs = [];
          return '/' . $original_path;
        }
        $elems = array_merge(array_slice(explode('/', $this->cache[$temp_path]), 1), array_slice($elems, $i));
        break;
      }
    }

    if ($elems[0] == 'mm' && count($elems) >= 2 && is_numeric($elems[1])) {
      $in_mm = TRUE;
      $this_mmtid = $parent = intval($elems[1]);
      array_shift($elems);
      array_shift($elems);
    }
    else if ($elems[0] == mm_home_mmtid()) {
      $in_mm = TRUE;
      $this_mmtid = $parent = mm_home_mmtid();
      array_shift($elems);
    }
    else if (count($elems) >= 2 && ($elems[0] == mm_content_users_alias() || is_numeric($elems[0]) && $elems[0] == mm_content_users_mmtid()) && strlen($elems[1]) == 1 && mm_get_setting('user_homepages.virtual')) {
      $in_mm = TRUE;
      $alias = ctype_alpha($elems[1][0]) ? strtoupper($elems[1][0]) : '~';
      array_splice($elems, 0, 2);
      $this_mmtid = $parent = count($elems) ? mm_content_users_mmtid() : -ord($alias);
    }
    else if (is_numeric($elems[0]) && intval($elems[0]) === 1) {
      return \Drupal::config('system.site')->get('page.404');
    }

    $joins = $wheres = $numeric = $args = $args_at_level = [];
    $a = 0;
    $reserved = mm_content_reserved_aliases();
    $max = min(count($elems), \Drupal::state()->get('monster_menus.mysql_max_joins', Constants::MM_CONTENT_MYSQL_MAX_JOINS));
    for ($i = 0; $i < $max; $i++) {
      $elem = $elems[$i];
      $numeric[$i] = FALSE;
      if (!$in_mm && $i == 0 && $assume_home && ($elem == 'settings' || $elem == 'contents')) {
        $in_mm = TRUE;
        $this_mmtid = mm_home_mmtid();
        break;
      }
      elseif (!in_array($elem, $reserved)) {
        $n = count($joins);
        $nprev = $n - 1;
        $joins[] = "{mm_tree} t$n" . ($n ? " ON t$n.parent = t$nprev.mmtid" : '');
        $prefix = $n ? '' : (empty($parent) ? '' : "t0.parent = $parent AND ");
        $middle = $n ? '' : (empty($parent) ? 't0.parent IN(1, ' . mm_home_mmtid() . ') AND ' : '');
        $numeric[$i] = is_numeric($elem) && intval($elem) == $elem && $elem != 0;
        $args[':a' . $a++] = $elem;
        if ($numeric[$i] && $elem > 0) {
          $wheres[] = "{$prefix}(t$n.mmtid = :a" . ($a - 1) . " OR {$middle}t$n.alias = :a$a)";
          $args[':a' . $a++] = $elem;
          $args_at_level[$i] = 2;
        }
        else {
          $wheres[] = "{$prefix}{$middle}t$n.alias = :a" . ($a - 1);
          $args_at_level[$i] = 1;
        }
      }
      else {
        break;
      }
    }

    $this->cache_oargs[$original_path] = [];
    while ($joins) {
      $n = count($joins) - 1;
      $new_mmtid = Database::getConnection()->query("SELECT t$n.mmtid FROM " . join(' INNER JOIN ', $joins) . ' WHERE ' . join(' AND ', $wheres) . " ORDER BY t$n.alias LIMIT 1", $args)->fetchField();
      array_pop($joins);
      array_pop($wheres);
      $was_numeric = array_pop($numeric);
      if ($new_mmtid) {
        $in_mm = TRUE;
        $this_mmtid = $new_mmtid;
        break;
      }

      if ($was_numeric) {
        if (intval($elems[0]) < 0) {
          $this_mmtid = $elems[0];
          $in_mm = TRUE;
          break;
        }
        elseif (!$joins) {
          $site_404 = \Drupal::config('system.site')->get('page.404');
          if ($site_404 && $site_404 != $original_path) {
            return $this->processInboundPath($path, $site_404);
          }
          break;
        }
      }
      // There can't be any extra args, so remove what's not needed
      if ($popped = array_pop($args_at_level)) {
        array_splice($args, -$popped);
      }
      $i--;
    }

    if ($in_mm && !empty($this_mmtid)) {
      $elems = array_slice($elems, $i);
      $oargs = $this->cache_oargs[$original_path] = $elems;
      array_unshift($elems, "mm/$this_mmtid");
      $path = implode('/', $elems);
    }
    else {
      // Path was in no way part of MM, so mark the topmost path element in
      // cache to prevent future attempts to match.
      if ($elems) {
        $this->cache[$elems[0]] = FALSE;
        $this->cache_oargs[$elems[0]] = [];
        return '/' . $path;
      }
    }

    return $this->cache[$original_path] = '/' . $path;
  }

  /**
   * Get the MMTID (tree ID) of the page described by an internal path.
   *
   * @param string $path
   *   URL to parse
   * @param array &$mmtids
   *   (optional) Array to receive the list of tree IDs
   * @param array &$oarg_list
   *   (optional) Array to receive the parameters following the tree IDs,
   *   un-parsed
   * @return int|null
   *   The tree ID of the page, or NULL if the page is not in MM.
   */
  public function getMmtidOfPath($path, &$mmtids = NULL, &$oarg_list = NULL) {
    if (mm_parse_args($mmtids, $oarg_list, $this_mmtid, $this->processInboundPath($path, $path)) == 'mm') {
      return $this_mmtid;
    }
    return NULL;
  }

  /**
   * Remove the special OARGS_KEY element from a URL query array.
   *
   * @param array $query
   *
   * @return array
   */
  public static function filterOargs($query) {
    unset($query[self::OARGS_KEY]);
    return $query;
  }

}

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

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