CNC router for Zend Framework and caching

Task:
To handle URLs of the form:

/ > www. example.com/what/about-this-2010-09-08.html

given the parameters in, for example, this pattern:

[module]/[controller]-[action]-[year]-[month]-[day].[ext]


How did the task.

1. Need to cache static and rekomendacia page in ZF

ZF, in addition to its versatility, also has a decent heaviness, and it is quite logical.
Therefore, when generating any content I try to apply caching to reduce the frequency of such generations.
Cache blocks, pages, object model, database queries — all that you can.

2. To give the pages generated .html, without any PHP involvement

Pages that do not allow the active content changes to better cache entirely. And of those, oddly enough, is a lot.
This created a Zend_Cache_Frontend_Page. The first time I have he has not earned, though not at all complicated. And instead of getting HADNT, I decided that such pages can be given without the participation of PHP, just like .html file.

Not to overextend .htacess complex rules, the folder structure will match queries, and Vice versa.
It is also desirable that the requests were humanized and not too long.
URLs:
the
/what/about/this.html
/what/about/other-12.html
/it/computing_games~page1.html
/it/computing_games~page1.html

file structure:
the
_cache/what/about/this.html
/other-12.html
_cache/it/computing_games~page1.html
/computing_games~page2.html

It remains only to specify ranting for such URLs, capture ob_start() the output for specific controllers / actions and save to the folder _cache

3. Ask for the ZF routes other than the standard

Here Zend offers a powerful tool Zend_Controller_Router_Route_Regex.
But the remedy is a little more than NOT readable. And even reverse and map to boot.

4. Write your class that handles

Wrote.
class EXT_Controller_Router extends Zend_Controller_Router_Route_Regex
{
protected $_defaultReqs = "\w+";
protected $_paramStrFormat = "name[value]";
protected $_paramStrValueGlue = "~";
//protected $_paramStrFormat = "name-value";
//protected $_paramStrValueGlue = "!";
protected $_regex = null;
protected $_route = null;
protected $_insides = array();
protected $_quotes = array();
protected $_defaults = array();
protected $_values = array();
protected $_map = array();
protected $_reqs = array();

/**
* @param Zend_Config $config Configuration object
*/

public static function getInstance(Zend_Config $config)
{
$defaults = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
$reqs = ($config->reqs instanceof Zend_Config) ? $config->reqs->toArray() : array();
}

public function __construct($route, $defaults = array(), $reqs = array())
{
$a = "(\[.*\])";
$b = "(\{.*\})";
$pattern = "#(.*)(" . $a . "|" . $b . ")#Ui";
$this->_quotes = array();
$this->_insides = array();
$map = array();
$regex = ";
if (preg_match_all($pattern, $route, $matches, PREG_SET_ORDER)) {
foreach ($matches as $i => $match) {
$inside = substr($match[2], 1, strlen($match[2])-2);
$key = preg_replace('[\W]', ", $inside);
if (!empty($reqs[$key])) {
$reg = $reqs[$key];
} elseif ('paramstr' == $key) {
$reg = '.*';
} else {
$reg = $this->_defaultReqs;
}
switch ($match[2][0]) {
case '[':
$end = ";
break;
case '{':
$end = '{0,1}';
break;
$regex .= preg_quote($match[1], '#') . '(' . str_replace($key, $reg, preg_quote($inside, '#')) . ')' . $end;
$map[$key] = $i+1;
$this->_quotes[$key] = $match[2][0];
$this->_insides[$key] = $inside;
}
}
preg_match("#[^\]\}]*$#i", $route, $lostMatch);
$regex .= preg_quote($lostMatch[0], '#');

$this->_route = $route;
$this->_regex = $regex;
$this->_defaults = (array) $defaults;
$this->_map = $map;
$this->_reqs = $reqs;
}

/**
* Matches a user submitted path with a previously defined route.
* Assigns and returns an array of defaults on a successful match.
*
* @param string $path Path used to match against this routing map
* @return array|false An array of assigned values or a false on a mismatch.
*/

public function match($path, $partial = false)
{
$path = trim(urldecode($path), '/');
$regex = '#^' . $this->_regex . '$#Ui';
$res = preg_match($regex, $path, $values);
if ($res === 0) {
return false;
}
foreach ($values as $i => $value) {
if (!is_int($i) || $i === 0 || empty($value)) {
}
}
$values = $this->_getMappedValues($values);

foreach ($this->_insides as $name => $ins) {
if ($name !== $ins && isset($values[$name])) {
$pattern = '#^' . str_replace($name, '(.*)', preg_quote($ins)) . '$#i';
if (preg_match($pattern, $values[$name], $matches)) {
$values[$name] = $matches[1];
}
}
}

if (!empty($values['paramstr'])) {
$values += $this->explodeParamStr($values['paramstr']);
}
$this->_values = $values;

$defaults = $this->_getMappedValues($this->_defaults, false, true);
$result = $values + $defaults;
return $result;
}

public function explodeParamStr($paramStr)
{
$values = array();
$pattern = str_replace(array('name', 'value'),
array('(\w+)', '(.*)'),
preg_quote($this->_paramStrFormat, '#'));

$pattern = '#(' . $pattern . ')#Uui';
if (preg_match_all($pattern, $paramStr, $matches, PREG_SET_ORDER)) {
if (!empty($match[3]))
$value = explode($this->_paramStrValueGlue, $match[3]);
if (count($value) > 1) {
$values[$match[2]] = $value;
} else {
$values[$match[2]] = $match[3];
}
}
}
return $values;
}

public function implodeParamStr(array $params)
{
$paramStr = ";
foreach($params as $name => $value) {
if (!in_array($name, array('controller', 'module', 'paramstr', 'action')) && !array_key_exists($name, $this->_map)) {

if (is_array($value)) {
$value = implode($this->_paramStrValueGlue, $value);
}
$paramStr .= str_replace(array('name', 'value'),
array($name, $value),
$this->_paramStrFormat);
}
}
if (empty($paramStr)) {
$paramStr = null;
}
return $paramStr;
}

/**
* Assembles a URL path defined by this route.
*
* @param array $data An array of name (or index) and value pairs used as parameters
*/

public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
{
$replaces = array();
$searches = array();

$paramStr = $this->implodeParamStr($data);

$data = $data + $this->_values;

if (empty($paramStr)) {
unset($data['paramstr']);
} else {
$data['paramstr'] = $paramStr;
}

foreach($this->_map as $key => $i) {
$inside = $this->_insides[$key];
switch ($this->_quotes[$key]) {
case '[':
$searches[$i] = '[' . $inside . ']';
break;
case '{':
$searches[$i] = '{' . $inside . '}';
break;
default:
break;
}
if (!empty($data[$key])) {
$replaces[$i] = str_ireplace($key, $data[$key], $inside);
} else {
$replaces[$i] = ";
}
}
$url = str_replace($searches, $replaces, $this->_route);
}
}

Usage

Provide a template url through routes.ini
[module]/[controller]/{action} {page}.htm
the
my-route.type = "EXT_Controller_Router"
my-route.route = "[module]/[controller]/{action} {page}.htm"
my-route.defaults.action = index

this route is suitable for queries of the form:
the
www.notmysite.ru/what/about/this.html
www.notmysite.ru/what/about/other-12.html

In square brackets the required parameters is [param_name]
in figure — optional {my_param}
Non-alphabetical characters inside the brackets in addition to the ID may be needed for:
separation parameters [action]{-page} if not specified any of the characters* consist therein
if url e need these brackets
www.notmysite.ru/dogovor[123e][12].htm
, then the route is specified like [controller][[tom]][[page]].htm
the
dogovor.route = "[controller][[tom]][[page]].htm"
dogovor.defaults.module = index
dogovor.defaults.controller = dogovor
dogovor.defaults.action = show

Magic [paramstr]

To pass a variable number of parameters I reserved identifier {paramstr},
It includes not expressly granted options.
For example, for the route:
[section]/{action}{~paramstr}.html
the
car.type = "EXT_Controller_Router"
car.route = "[section]/{action}{~paramstr}.html"
car.defaults.module = car
car.defaults.controller = catalog

fit request
/car/view~year[2001~2003]fuel[diesel]color[blue~red]vol[1000~1600]state[good]place[mosque].html
and in the controller Test_CatalogController
we get all the parameters:
the
'section' => 'car' 
'action' => 'view' 
'paramstr' => 'year' [2001~2003]fuel[diesel]color[blue-red]vol[1000~1600]state[good]place[Moscow]' 
'letter' => 'l' 
'year' 
0 => '2001' 
1 => '2003' 

'fuel' => 'diesel' 
'color'
0 => 'blue' 
1 => 'red' 
'vol' 
0 => '1000' 
1 => '1600' 

'state' => 'good' 
'place' => 'Moscow' 
'module' => 'test' 
'controller' => 'index' 

And back if to pass to the view helper these settings will generate the appropriate url.
the
$params = array('action' => 'view', 
'step' => 2, 
'year' => array('2009', '2010'));
$nextUrl = $this->view->url($params, 'my-route');


Details

1. As in Zend_Controller_Router_Route_Regex, you can use templates .reqs for each parameter
the
for example, "a limited set of values":
my-route.reqs.action = "index|view|print" 

or "only numbers":
my-route.reqs.action = "\d+" 


2. As shown above, the parameters {paramstr} are transmitted in the format key1[value1~value2]key2[value1~value2~value3]
This format can be changed in the classroom
the
protected $_paramStrFormat = "name[value]";
protected $_paramStrValueGlue = "~";


Important

The class workable, but not perfect.
In the future it can be corrected, to get rid of excess preg_match inherit from Regex, but from the Abstract
This class complements sendowski ranting, so for standard cases it is better to use standard tools. But if you love yourself to write regular expressions for URLs, it is generally better not to use this class.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Automatically create Liquibase migrations for PostgreSQL

Vkontakte sync with address book for iPhone. How it was done

What part of the archived web