title: PHP preg_match for routing
body: <p>I have ventured into a number of other questions here but couldn't find an actual answer to my scenario.</p>
  
  <p>I am working on modifying my framework a little to have multiple "applications" run from the same framework base structure. Essentially this will change my url structure which is a little complicated for me to approach at the moment. </p>
  
  <p>I have the following potential url structures:</p>
  
  <ol>
  <li>:module\:controller\:action\:properties</li>
  <li>:controller\action\:properties</li>
  <li>:module\controller</li>
  <li>:controller</li>
  </ol>
  
  <p>The problem is that I need to be able to determine while routing if the first parameter is indeed a module call (such as cms) or if it's a controller call which uses the "default" module (site).</p>
  
  <p>An example here would be:</p>
  
  <ol>
  <li><a href="http://domain.com/cms/pages/" rel="nofollow">http://domain.com/cms/pages/</a> (needs to use cms as module, pages for controller and defaults to index as action)</li>
  <li><a href="http://domain.com/articles/view/11" rel="nofollow">http://domain.com/articles/view/11</a> (needs to use site as the module (default module), articles as controller, view as action and 11 as property).</li>
  </ol>
  
  <p>My 2nd issue I have is that my preg_match at the moment is strict so I need to add a route for each possible scenario, which can be become quite lengthy and I am sure there is a better solution to this.</p>
  
  <p>As an example I have a route setup for /:module/:controller which will match say /cms/users but in some cases I may need to match partial routes and route a user to another place. Say the requested module and controller exists but the action doesn't. If I want to setup /cms/:controller/:action and the :action does not exists I do not want it to skip it as a match, but rather want to setup a fallback for that specific match to say default to /cms/notfound/ or something.</p>
  
  <p>Here is my code I use at the moment:</p>
  
  <pre><code> // EXAMPLE OF MY ROUTES (used within the router object for matching):
  var $routes = array(
      "/" =&gt; ["base" =&gt; "\application\site\controllers","controller" =&gt; "static", "action" =&gt; "index","properties" =&gt; ""],
      "/cms" =&gt; ["base" =&gt; "\application\cms\controllers","controller" =&gt; "users", "action" =&gt; "login","properties" =&gt; ""],
      "/cms/:controller/:action" =&gt; ["base" =&gt; "\application\cms\controllers","controller" =&gt; "", "action" =&gt; "","properties" =&gt; ""]
  );
  
   // MATCH METHOD IN ROUTER:
  
   public function match($request) {
  
   // $request can be anything from / to /users/login 
  
      if (($strpos = strpos($request, '?')) !== false) {
          $request = substr($request, 0, $strpos);
      }
  
   // Tokens used within router:
   $tokens = array(
          ":application" =&gt; "(?P&lt;application&gt;[a-zA-Z\-\_]+)",
          ":controller" =&gt; "(?P&lt;controller&gt;[a-zA-Z\-\_]+)",
          ":action" =&gt; "(?P&lt;action&gt;[a-zA-Z\-\_]+)",
          ":properties" =&gt; "(?P&lt;properties&gt;[a-zA-Z0-9\-\_\=\;]+)",
          ":id" =&gt; "([0-9])"
      );
  
      foreach ($this-&gt;routes as $route =&gt; $properties) {
          $pattern = '#^/?' . strtr($route, $tokens) . '/?$#';
          if (preg_match($pattern, $request, $matches)) {
  
              unset($matches[0]);
              // Set the application:
              if (!isset($matches[1])) {
                  $match['application'] = $this-&gt;config-&gt;default_application;
              } else {
                  if (in_array($matches[1], $this-&gt;config-&gt;applications)) {
                      $match['application'] = $matches[1];
                  } else {
                      return false;
                  }
              }
  
              // Set the controller and action:
              if (array_key_exists($match['application'], $this-&gt;config-&gt;application_routes)) {
                  $match['controller'] = (isset($matches[2]) ? $matches[2] : $this-&gt;config-&gt;application_routes[$match['application']]['controller']);
                  $match['action'] = (isset($matches[3]) ? $matches[3] : $this-&gt;config-&gt;application_routes[$match['application']]['action']);
              }
  
              // Set the properties:
              $match['properties'] = (isset($matches[4]) ? $this-&gt;generate_properties($matches[4]) : false);
              return $match;
          }
      }
      return false;
  }
  </code></pre>
  
  <p>So essentially I need the following advice:</p>
  
  <ol>
  <li>Best way to setup the returning $match array (to assign module, controller, action and properties)</li>
  <li>How to make preg_match to be less "strict" (so allow for partial matches and rather have it route to another key within my map for say "notfound" etc.</li>
  </ol>