Commit 9b821cd3 authored by alexpott's avatar alexpott

Issue #2272201 by kim.pepper: Move drupal_match_path in path.inc to a service.

parent d5be5c04
......@@ -403,6 +403,10 @@ services:
path.alias_storage:
class: Drupal\Core\Path\AliasStorage
arguments: ['@database', '@module_handler']
path.matcher:
class: Drupal\Core\Path\PathMatcher
arguments: ['@config.factory']
# The argument to the hashing service defined in services.yml, to the
# constructor of PhpassHashedPassword is the log2 number of iterations for
# password stretching.
......
......@@ -322,6 +322,9 @@ function install_begin_request(&$install_state) {
$container
->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
->addArgument(new Reference('language_manager'));
$container
->register('path.matcher', 'Drupal\Core\Path\PathMatcher')
->addArgument(new Reference('config.factory'));
\Drupal::setContainer($container);
......
......@@ -41,27 +41,12 @@ function drupal_is_front_page() {
*
* @return
* Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
*
* @deprecated as of Drupal 8.0. Use
* \Drupal\Core\Path\PathMatcherInterface::matchPath() instead.
*/
function drupal_match_path($path, $patterns) {
$regexps = &drupal_static(__FUNCTION__);
if (!isset($regexps[$patterns])) {
// Convert path settings to a regular expression.
// Therefore replace newlines with a logical or, /* with asterisks and the <front> with the frontpage.
$to_replace = array(
'/(\r\n?|\n)/', // newlines
'/\\\\\*/', // asterisks
'/(^|\|)\\\\<front\\\\>($|\|)/' // <front>
);
$replacements = array(
'|',
'.*',
'\1' . preg_quote(\Drupal::config('system.site')->get('page.front'), '/') . '\2'
);
$patterns_quoted = preg_quote($patterns, '/');
$regexps[$patterns] = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/';
}
return (bool)preg_match($regexps[$patterns], $path);
return \Drupal::service('path.matcher')->matchPath($path, $patterns);
}
/**
......
<?php
/**
* @file
* Contains \Drupal\Core\Path\PathMatcher.
*/
namespace Drupal\Core\Path;
use Drupal\Core\Config\ConfigFactoryInterface;
/**
* Provides a path matcher.
*/
class PathMatcher implements PathMatcherInterface {
/**
* The default front page.
*
* @var string
*/
protected $frontPage;
/**
* The cache of regular expressions.
*
* @var array
*/
protected $regexes;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Creates a new PathMatcher.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
*/
public function __construct(ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
public function matchPath($path, $patterns) {
if (!isset($this->regexes[$patterns])) {
// Lazy-load front page config.
if (!isset($this->frontPage)) {
$this->frontPage = $this->configFactory->get('system.site')->get('page.front');
}
// Convert path settings to a regular expression.
$to_replace = array(
// Replace newlines with a logical 'or'.
'/(\r\n?|\n)/',
// Quote asterisks.
'/\\\\\*/',
// Quote <front> keyword.
'/(^|\|)\\\\<front\\\\>($|\|)/',
);
$replacements = array(
'|',
'.*',
'\1' . preg_quote($this->frontPage, '/') . '\2',
);
$patterns_quoted = preg_quote($patterns, '/');
$this->regexes[$patterns] = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/';
}
return (bool) preg_match($this->regexes[$patterns], $path);
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Path\PathMatcherInterface
*/
namespace Drupal\Core\Path;
/**
* Provides an interface for URL path matchers.
*/
interface PathMatcherInterface {
/**
* Checks if a path matches any pattern in a set of patterns.
*
* @param string $path
* The path to match.
* @param string $patterns
* A set of patterns separated by a newline.
*
* @return bool
* TRUE if the path matches a pattern, FALSE otherwise.
*/
public function matchPath($path, $patterns);
}
......@@ -10,6 +10,7 @@
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Path\PathMatcherInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -32,6 +33,13 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
*/
protected $aliasManager;
/**
* The path matcher.
*
* @var \Drupal\Core\Path\PathMatcherInterface
*/
protected $pathMatcher;
/**
* The request stack.
*
......@@ -44,6 +52,8 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
*
* @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
* An alias manager to find the alias for the current system path.
* @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
* The path matcher service.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param array $configuration
......@@ -53,9 +63,10 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
* @param array $plugin_definition
* The plugin implementation definition.
*/
public function __construct(AliasManagerInterface $alias_manager, RequestStack $request_stack, array $configuration, $plugin_id, array $plugin_definition) {
public function __construct(AliasManagerInterface $alias_manager, PathMatcherInterface $path_matcher, RequestStack $request_stack, array $configuration, $plugin_id, array $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->aliasManager = $alias_manager;
$this->pathMatcher = $path_matcher;
$this->requestStack = $request_stack;
}
......@@ -65,6 +76,7 @@ public function __construct(AliasManagerInterface $alias_manager, RequestStack $
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('path.alias_manager.cached'),
$container->get('path.matcher'),
$container->get('request_stack'),
$configuration,
$plugin_id,
......@@ -128,22 +140,6 @@ public function evaluate() {
$path = $request->attributes->get('_system_path');
$path_alias = Unicode::strtolower($this->aliasManager->getAliasByPath($path));
return $this->matchPath($path_alias, $pages) || (($path != $path_alias) && $this->matchPath($path, $pages));
return $this->pathMatcher->matchPath($path_alias, $pages) || (($path != $path_alias) && $this->pathMatcher->matchPath($path, $pages));
}
/**
* Check if a path matches any pattern in a set of patterns.
*
* @param string $path
* The path to match.
* @param string $patterns
* String containing a set of patterns separated by \n, \r or \r\n.
*
* @return bool
* TRUE if the path matches a pattern, FALSE otherwise.
*/
protected function matchPath($path, $patterns) {
return drupal_match_path($path, $patterns);
}
}
<?php
/**
* @file
* Definition of Drupal\system\Tests\Path\MatchPathTest.
*/
namespace Drupal\system\Tests\Path;
use Drupal\simpletest\WebTestBase;
/**
* Unit tests for the drupal_match_path() function in path.inc.
*
* @see drupal_match_path().
*/
class MatchPathTest extends WebTestBase {
protected $front;
public static function getInfo() {
return array(
'name' => 'Drupal match path',
'description' => 'Tests the drupal_match_path() function to make sure it works properly.',
'group' => 'Path API',
);
}
function setUp() {
// Set up the database and testing environment.
parent::setUp();
// Set up a random site front page to test the '<front>' placeholder.
$this->front = $this->randomName();
\Drupal::config('system.site')->set('page.front', $this->front)->save();
// Refresh our static variables from the database.
$this->refreshVariables();
}
/**
* Run through our test cases, making sure each one works as expected.
*/
function testDrupalMatchPath() {
// Set up our test cases.
$tests = $this->drupalMatchPathTests();
foreach ($tests as $patterns => $cases) {
foreach ($cases as $path => $expected_result) {
$actual_result = drupal_match_path($path, $patterns);
$this->assertIdentical($actual_result, $expected_result, format_string('Tried matching the path <code>@path</code> to the pattern <pre>@patterns</pre> - expected @expected, got @actual.', array('@path' => $path, '@patterns' => $patterns, '@expected' => var_export($expected_result, TRUE), '@actual' => var_export($actual_result, TRUE))));
}
}
}
/**
* Helper function for testDrupalMatchPath(): set up an array of test cases.
*
* @return
* An array of test cases to cycle through.
*/
private function drupalMatchPathTests() {
return array(
// Single absolute paths.
'example/1' => array(
'example/1' => TRUE,
'example/2' => FALSE,
'test' => FALSE,
),
// Single paths with wildcards.
'example/*' => array(
'example/1' => TRUE,
'example/2' => TRUE,
'example/3/edit' => TRUE,
'example/' => TRUE,
'example' => FALSE,
'test' => FALSE,
),
// Single paths with multiple wildcards.
'node/*/revisions/*' => array(
'node/1/revisions/3' => TRUE,
'node/345/revisions/test' => TRUE,
'node/23/edit' => FALSE,
'test' => FALSE,
),
// Single paths with '<front>'.
'<front>' => array(
$this->front => TRUE,
"$this->front/" => FALSE,
"$this->front/edit" => FALSE,
'node' => FALSE,
'' => FALSE,
),
// Paths with both '<front>' and wildcards (should not work).
'<front>/*' => array(
$this->front => FALSE,
"$this->front/" => FALSE,
"$this->front/edit" => FALSE,
'node/12' => FALSE,
'' => FALSE,
),
// Multiple paths with the \n delimiter.
"node/*\nnode/*/edit" => array(
'node/1' => TRUE,
'node/view' => TRUE,
'node/32/edit' => TRUE,
'node/delete/edit' => TRUE,
'node/50/delete' => TRUE,
'test/example' => FALSE,
),
// Multiple paths with the \r delimiter.
"user/*\rexample/*" => array(
'user/1' => TRUE,
'example/1' => TRUE,
'user/1/example/1' => TRUE,
'user/example' => TRUE,
'test/example' => FALSE,
'user' => FALSE,
'example' => FALSE,
),
// Multiple paths with the \r\n delimiter.
"test\r\n<front>" => array(
'test' => TRUE,
$this->front => TRUE,
'example' => FALSE,
),
// Test existing regular expressions (should be escaped).
'[^/]+?/[0-9]' => array(
'test/1' => FALSE,
'[^/]+?/[0-9]' => TRUE,
),
);
}
}
<?php
/**
* @file
* Contains Drupal\Tests\Core\Path\PathMatcherTest
*/
namespace Drupal\Tests\Core\Path;
use Drupal\Component\Utility\String;
use Drupal\Core\Path\PathMatcher;
use Drupal\Tests\UnitTestCase;
/**
* @group Drupal
* @see \Drupal\Core\Path\PathMatcher
*/
class PathMatcherTest extends UnitTestCase {
/**
* The path matcher under test.
*
* @var \Drupal\Core\Path\PathMatcher
*/
protected $pathMatcher;
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Path Matcher tests',
'description' => 'Tests that path matching is working properly.',
'group' => 'Path',
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
// Create a stub config factory with all config settings that will be
// checked during this test.
$config_factory_stub = $this->getConfigFactoryStub(
array(
'system.site' => array(
'page.front' => 'dummy',
),
)
);
$this->pathMatcher = new PathMatcher($config_factory_stub);
}
/**
* Test that standard paths works with multiple patterns.
*
* @dataProvider getMatchPathData
*/
public function testMatchPath($patterns, $paths) {
foreach ($paths as $path => $expected_result) {
$actual_result = $this->pathMatcher->matchPath($path, $patterns);
$this->assertEquals($actual_result, $expected_result, String::format('Tried matching the path <code>@path</code> to the pattern <pre>@patterns</pre> - expected @expected, got @actual.', array(
'@path' => $path,
'@patterns' => $patterns,
'@expected' => var_export($expected_result, TRUE),
'@actual' => var_export($actual_result, TRUE),
)));
}
}
/**
* Provides test path data.
*
* @return array
* A nested array of pattern arrays and path arrays.
*/
public function getMatchPathData() {
return array(
array(
// Single absolute paths.
'example/1',
array(
'example/1' => TRUE,
'example/2' => FALSE,
'test' => FALSE,
),
),
array(
// Single paths with wildcards.
'example/*',
array(
'example/1' => TRUE,
'example/2' => TRUE,
'example/3/edit' => TRUE,
'example/' => TRUE,
'example' => FALSE,
'test' => FALSE,
),
),
array(
// Single paths with multiple wildcards.
'node/*/revisions/*',
array(
'node/1/revisions/3' => TRUE,
'node/345/revisions/test' => TRUE,
'node/23/edit' => FALSE,
'test' => FALSE,
),
),
array(
// Single paths with '<front>'.
"<front>",
array(
'dummy' => TRUE,
"dummy/" => FALSE,
"dummy/edit" => FALSE,
'node' => FALSE,
'' => FALSE,
),
),
array(
// Paths with both '<front>' and wildcards (should not work).
"<front>/*",
array(
'dummy' => FALSE,
'dummy/' => FALSE,
'dummy/edit' => FALSE,
'node/12' => FALSE,
'' => FALSE,
),
),
array(
// Multiple paths with the \n delimiter.
"node/*\nnode/*/edit",
array(
'node/1' => TRUE,
'node/view' => TRUE,
'node/32/edit' => TRUE,
'node/delete/edit' => TRUE,
'node/50/delete' => TRUE,
'test/example' => FALSE,
),
),
array(
// Multiple paths with the \r delimiter.
"user/*\rexample/*",
array(
'user/1' => TRUE,
'example/1' => TRUE,
'user/1/example/1' => TRUE,
'user/example' => TRUE,
'test/example' => FALSE,
'user' => FALSE,
'example' => FALSE,
),
),
array(
// Multiple paths with the \r\n delimiter.
"test\r\n<front>",
array(
'test' => TRUE,
'dummy' => TRUE,
'example' => FALSE,
),
),
array(
// Test existing regular expressions (should be escaped).
'[^/]+?/[0-9]',
array(
'test/1' => FALSE,
'[^/]+?/[0-9]' => TRUE,
),
),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment