将URI与当前URL匹配

时间:2016-02-19 20:17:16

标签: php regex

我制作了一个简单的路由器系统,我正在尝试将当前的uri与网址匹配,请检查:

$listUri = "transaction/.+";
$uri = isset($_REQUEST['uri']) ? $_REQUEST['uri'] : '/'; // transaction/19-02-2016

if(preg_match("#^$listUri$#", $uri)) 
{
    echo "done!";
}

现在我正确地看到了echo "done!";但是假设我认为我有这种情况:

$listUri = "transaction/.+";
$uri = isset($_REQUEST['uri']) ? $_REQUEST['uri'] : '/'; // transaction/19-02-2016/SomeWrongUrlRequest?

if(preg_match("#^$listUri$#", $uri)) 
{
    echo "done!";
}

echo "done!"也被打印出......这很糟糕。我已经映射了这样的网址:"/transaction/.+";其中.+是其后面的参数19-02-2016,如果.+请求之后有更多内容必须不正确。< / p>

更新:

换句话说:

$uri     =>  "transaction/19-02-2016/SomeWrongUrlRequest"
$listUri =>  "transaction/.+"

如果$listUritransaction/.+,我必须这样:

`transaction/19-02-2016/` (correct with or without final slash)

`transaction/19-02-2016/SomeWrongUrlRequest` (incorrect - there is only a .+ it would have been correct if $listUri had been: transaction/.+/SomeWrongUrlRequest)

所以我必须使匹配内容等于当前URI

1 个答案:

答案 0 :(得分:3)

从我看到你正在制作(或已经制作)路由系统。

但我觉得这里的问题不会通过找到正确的正则表达式来修复,但我认为可以通过找到正确的方法来创建路由系统来解决。

您需要有某种策略来确定在命中特定URL时应执行哪条路由。让我自己清楚 假设程序员使用您的路由器想要以下场景:

  1. 网址 - &gt; /user/.+

    结果 - &gt;嘿来宾!

  2. 网址 - &gt; /user/.+/{regex_matching_username}

    结果 - &gt;嘿用户名!

  3. 现在,如果网址类似于/user/free/john,路由系统将如何决定要转到哪个网址?与您的情况类似,网址#1仍会与此网址匹配,并会继续说Hey Guest!

    因此我们需要定义一个应该执行路由的优先级,这可以是它们的定义顺序(因此路由存储在堆栈或队列中),也可能是某种优先级值分配给它们每条路由(优先级队列中的路由)。

    与ZF1和Laravel合作后,我可以讲述他们采取的方法:

    ZF1明确提到

      

    注意:反向匹配

         

    路由按相反顺序匹配,因此make   确保首先定义最通用的路线。

    因此,如果您在ZF1的最后一个路线中定义了user/.+这样的通用路线,那么您的所有其他路线都无法正常工作。

    在Laravel中虽然我无法在文档中找到它,但它们似乎遵循路线定义的顺序。我正在粘贴一个例子,以防万一你想看看。

    // matches a url that has username starting with a
    Route::get('user/{name}', ['as' => 'profile', function()
    {
        //
        echo ' I am specific';
    }])->where('name', 'a.+');;
    
    Route::get('user/{ame}', ['as' => 'profile', function()
    {
        //
        echo ' I am  generic';
    }])->where('ame', '.+');
    
      

    网址 - &gt; #/ user / abc

         

    输出 - &gt;我具体是

         

    网址 - &gt; #/ user / bbc

         

    输出 - &gt;我是通用的

    事情按预期工作,但现在扭转了特定和通用路线的顺序

    Route::get('user/{ame}', ['as' => 'profile', function()
    {
        //
        echo ' I am  generic';
    }])->where('ame', '.+');
    
    // matches a url that has username starting with a
    Route::get('user/{name}', ['as' => 'profile', function()
    {
        //
        echo ' I am specific';
    }])->where('name', 'a.+');;
    
      

    网址 - &gt; #/ user / abc

         

    输出 - &gt;我是通用的

         

    网址 - &gt; #/ user / bbc

         

    输出 - &gt;我是通用的

    现在作为通用路线在顶部,两个URL都会产生相同的输出。

    如上所述,您仍然可以通过在/的基础上分解正则表达式和URL,然后匹配两个字符串的每个非空部分来满足您的特定情况。 psuedo代码中的这个可能看起来像这样

    将$ matcher字符串与当前网址匹配

    1. 根据/

    2. 分解$ matcher和$ url
    3. 检查$ matcher&amp;的非空部分数量。 $ url是平等的。

      如果是,继续第3步

      如果没有return false $匹配器不匹配

    4. 使用preg_match检查$ matl的每个部分和$ url的每个部分。

      如果所有部分都匹配return true $ matcher是正确的路线

      如果任何一个部分不匹配return false $匹配器不是正确的路线。

    5. 我希望这一切都有道理:)

      更新:为上面提到的伪代码添加一些代码

      function matchRoute($url, $pattern) {
              // get parts of the url
              $urlParts = array_filter(explode('/', $url));
              $patternParts = array_filter(explode('/', $pattern)); 
              // match if number of parts are equal
              if (count($urlParts) != count($patternParts)) {
                  return false;
              }
      
              // preg match in a loop
              for ($i = 0 ; $i < count($urlParts); $i++) {
                  if(!preg_match('/' . $patternParts[$i] .'/', $urlParts[$i])) {
                      return false;
                  }
              }
      
              return true;
      
      }
      
      $testUri = 'transaction/19-02-2016/SomeWrongUrlRequest';
      $matchUri = 'transaction/.+';
      
      echo "expected false \n";
      var_dump(matchRoute($testUri, $matchUri));  
      echo "expected true \n";
      var_dump(matchRoute('transaction/19-02-2016', $matchUri)); 
      echo "expected true \n";
      var_dump(matchRoute('transaction/19-02-2016/', $matchUri));
      
      echo "expected true \n";
      var_dump(matchRoute($testUri, 'transaction/.+/SomeWrongUrlRequest')); 
      
      echo "expected false \n";
      var_dump(matchRoute($testUri, 'transaction/.+/SomeOtherUrlRequest')); 
      

      输出:

      expected false 
      bool(false)
      expected true 
      bool(true)
      expected true 
      bool(true)
      expected true 
      bool(true)
      expected false 
      bool(false)
      

      现在上面写的代码不是最好的解决方案,我可以立即看到的一些问题是:

      1. 您无法再匹配通用路线,因为现在您需要明确定义网址所包含的部分数量

      2. array_filter会检查非空值,因此会排除/0之类的部分。 (虽然这可以通过使用自定义回调来处理)

      3. 如果您确定您的方案已完全填满,请使用上述内容。