解决了:
感谢@SJFrK,我的问题已经解决。下面的课程让我可以做我需要的事情。问题跟在课堂后面。
class Redirects
{
/*
|--------------------------------------------------------------------------
| Placeholder Types
|--------------------------------------------------------------------------
|
| An array of placeholder types thatalow specific characters in a source
| route or URL.
|
*/
private static $placeholders = array(
':all' => '.*',
':alpha' => '[a-z]+',
':alphanum' => '[a-z0-9]+',
':any' => '[a-z0-9\.\-_%=]+',
':num' => '[0-9]+',
':segment' => '[a-z0-9\-\_]+',
':segments' => '[a-z0-9\-\_\/]+',
);
/*
|--------------------------------------------------------------------------
| Computed Replacements
|--------------------------------------------------------------------------
|
| An array that contains converted source placeholders.
|
*/
private static $computed_replacements = array();
/*
|--------------------------------------------------------------------------
| Computed Replacements
|--------------------------------------------------------------------------
|
| Class-scoped string that contains a replacement route or URL from
| site.redirects.
|
*/
private static $destination;
/**
* Check for a redirect. If it exists, then redirect to it's computed replacement.
*
* @return Response
*/
public static function redirect()
{
// Only do this if redirects have been defined.
if (Config::has('site.redirects') && count(Config::get('site.redirects') > 0))
{
$route = URI::current();
// Get the available placeholders from static::$placeholders and
// convert them into applicable options for the pattern.
$available_placeholders = '';
foreach (static::$placeholders as $placeholder => $expression)
$available_placeholders .= ltrim("$placeholder|", ':');
$available_placeholders = rtrim($available_placeholders, '|');
// Define the pattern.
$pattern = '/\((\w+):('.$available_placeholders.')\)/';
// Get the redirects.
$redirects = Config::get('site.redirects');
// For each redirect, convert all the place holders and resulting
// values, and check for a match. If one exists, then redirect to it.
foreach ($redirects as $from => $to) {
static::$computed_replacements = array();
static::$destination = $to;
$from = rtrim($from, '/');
$to = rtrim($to, '/');
// Convert the placeholders in $from (if any)
$converted_placeholders = preg_replace_callback($pattern, function($captures) {
static::$computed_replacements[] = $captures[1];
return '('.static::$placeholders[":{$captures[2]}"].')';
}, $from);
// Get the replacements
$converted_replacements = preg_replace_callback("#^{$converted_placeholders}$#i", function($captures) {
$output = static::$destination;
for ($c = 1, $n = count($captures); $c < $n; ++$c)
{
$value = array_shift(static::$computed_replacements);
$output = str_replace("<$value>", $captures[$c], $output);
}
return $output;
}, $route);
// If the current route matches the converted expression, then redirect.
if (preg_match("!^{$converted_placeholders}$!i", $route))
return Redirect::to($converted_replacements, 301)
->with('moved-from', $route)
->with('moved-from-rule', "'$from': '".static::$destination."'");
}
}
else return;
}
}
介绍
我正在用Laravel (FTW) 编写一个静态网站包,并且遇到了一些我似乎无法解决的问题。
该应用程序包含一个配置文件 ( config/site.php
),其中 - 除其他外 - 包含一个重定向查找数组。这个想法是检查每个查找 URI 中的匹配项,然后重定向到替换 URI(当然,使用 301 重定向) - 对于那些将静态 HTML 站点移动到我的包的人很有用。
数组的格式为:
'redirects' => array(
'<source>' => '<destination>'
)
在 中<source>
,用户可以包括以下内容:
- 常用表达
- Laravel 风格的占位符:
(:any)
,(:all)
,(:num)
, 或(:alpha)
- 我添加了最后一个。
例如,用户可以使用以下重定向(请注意,我选择使用角括号,<destination>
因为它更美观一些 - 它被转换回它的正则表达式等效[参见下面的类]):
'(:all).html' => '<1>'
这会将任何以结尾的页面定向.html
到不包含它的路由。示例:http://example.com/about.html
将重定向到http://example.com/about
.
问题
我希望能够“命名”每个参数以便于阅读(和愉快的编码)。我想做的是命名每个占位符<source>
(当然这是可选的),并定义一个占位符类型。例如:
'(name:all).html' => '<name>'
现在,考虑到可以命名占位符,它们自然可以在 中按任何顺序排列<destination>
,例如(注意目标 URI 中的顺序变化):
'Products/(name:any)-(category:any)-(id:num).html' => 'products/<category>/<id>/<name>/overview'
使用常规占位符语法,我可以将其解释为:
'Products/(:any)-(:any)-(:num).html' => 'products/<2>/<3>/<1>'
无论如何,这是后备方案 - 我需要找出如何使用相应的捕获组替换名称preg_replace
。但似乎不可能使用命名参数/捕获:
命名参数
一个简单的方法是在 中使用命名参数preg_replace
,但是(据我所知)PHP 不支持它。
使用这种方法将使我能够使用相同类型的替换工具来完成任务 - 所以没有它可用有点令人失望。
尽管如此,我很高兴恢复到更复杂的东西(无论如何这是一件好事)。问题是,我不知道怎么做——而且我还没有找到解决方案。我看过Silex RouteCompiler
类,但不完全理解。我觉得,考虑到 Laravel 的主干是建立在 Symfony 组件集(很像 Silex)之上的,可能有更好的方法来完成我所需要的。
有没有人有同样的要求,也许找到了解决方案?这里的任何帮助都非常棒!提前致谢。
当前课程
这是处理重定向的类的源代码。我只是调用Redirects::handle()
我的routes.php
文件。
class Redirects
{
private static $placeholders = array(
':any' => '[a-zA-Z0-9\.\-_%=]+',
':num' => '[0-9]+',
':alpha' => '[a-z]+', //added
':all' => '.*',
);
private static $replacement_identifiers = array(
'(\<([0-9]+)\>)' => '$$1',
);
/**
* Create the applicable regex placeholders
*
* @param string &$from
* @param string &$to
* @return void
*/
protected static function placeholders(string &$from, string &$to)
{
// Replace the <source> with a match expression
foreach (static::$placeholders as $placeholder => $expression)
$from = str_replace("({$placeholder})", "({$expression})", $from);
// Replace the <destination> with a replacement expression
foreach (static::$replacement_identifiers as $identifier => $expression)
if (preg_match($identifier, $to))
$to = preg_replace($identifier, $expression, $to);
}
/**
* Return the response of any redirects, or void if none
*
* @return Response|void
*/
public static function handle()
{
$route = URI::current();
if (Config::has('site.redirects'))
{
$redirects = Config::get('site.redirects');
foreach ($redirects as $from => $to) {
$from = rtrim($from, '/');
static::placeholders($from, $to);
if (preg_match("!^{$from}$!i", $route))
{
if (strpos($to, '$') !== false and strpos($from, '(') !== false)
$to = preg_replace("!^{$from}$!i", $to, $route);
return Redirect::to($to, 301)->with('Moved-From', $route);
}
else return;
}
}
else return;
}
}