views:

84

answers:

2

Hi,

I would like to keep my port number which is different on production and development environment, but calls to the url helper based on my zend routes forget the port number.

My routes are a bunch of regexp routes, chaining with default hostname routes, mainly for multilanguage over a multidomain configuration (short overview below).

<?php
    $routecateg = new Zend_Controller_Router_Route_Regex('cat/(\w+)_([^_]+)(?:_page_(\d+))?(?:_par_(\d+))?(?:.html)?',
    array(1 =>'',2=>'',3=>'1',4=>'20','controller' =>'list','action'=>'categ'),
    array(1 =>'categid',2=>'categname',3=>'page',4=>'par'),
    'cat/%s_%s_page_%d_par_%d.html'
);

$routeindex= new Zend_Controller_Router_Route_Regex('(index|home)?',
    array('controller' =>'index','action'=>'home'),
    array(),
    'index'
);    

$hostRouteRepository = new Zend_Controller_Router_Route_Hostname(
    ':lang.'.$config->serverurl
);
$router ->addRoute('index',$hostRouteRepository->chain($routeindex));
$router ->addRoute('categ',$hostRouteRepository->chain($routecateg));
?>

Where $config->serverurl is just the domainname depending on environment and configured in my application.ini file.

On my production server, it's ok, as i am running on default port 80, but on developmenet, I need to run on a different port, and each time i call my url helper, port number is forgotten.

I know i can workaround by better configuring my apache server, but i'm surprised to not find any solutions to this issue.

A: 

Here is what I found:

If you pass something like ':lang.example.com:8888' or ':lang.example.com:port' to the constructor of Zend_Controller_Router_Route_Hostname, the port part won't be correctly parsed (com:8888 or com:port). This is due to the fact that the string is exploded with the '.' character and that the hostVariable character (':') is only checked on the first character of the exploded parts in construct:

foreach (explode('.', $route) as $pos => $part) {
            if (substr($part, 0, 1) == $this->_hostVariable) {
                $name = substr($part, 1);
                $this->_parts[$pos] = (isset($reqs[$name]) ? $reqs[$name]
                                       : $this->_defaultRegex);
                $this->_variables[$pos] = $name;
            } else {
                $this->_parts[$pos] = $part;
                $this->_staticCount++;
            }
        }

Now, in the route matching function (function match($request)), the port number is discarded from the request which will prevent valid requests to match the route:

// Get the host and remove unnecessary port information
    $host = $request->getHttpHost();
    if (preg_match('#:\d+$#', $host, $result) === 1) {
        $host = substr($host, 0, -strlen($result[0]));
    }

I believe that there are 3 different ways to solve your problem:

  1. Add a port number option in your config.ini file and register it to the Zend_Registry with something like: Zend_Registry::set('PORT_NUMBER', $this->getOption('portnumber'));
  2. Fix the code in the function match($request) to keep the port number (commenting out the code shown above should do it)
  3. Fix the code in the constructor to allow for ':port' parametrization of the route (you might want to use a required or a default value when setting up your route)

Note: solution 2 seems easier than 3 but some additional work is needed because the port number will be urlencoded by the assemble function:

foreach (array_reverse($host, true) as $key => $value) {
        if ($flag || !isset($this->_variables[$key]) || $value !== $this->getDefault($this->_variables[$key]) || $partial) {
            if ($encode) $value = urlencode($value);
            $return = '.' . $value . $return;

            $flag = true;
        }
    }

This should not happen with solution 3 because the port number is a variable.

Hope that helps.

Kenji Baheux
A: 

Hey jmeyo, thanks for asking that question. I managed to find a nice example for the hostname rule.

lordspace