Source for file jSignificantUrlsCompiler.class.php

Documentation is available at jSignificantUrlsCompiler.class.php

  1. <?php
  2. /**
  3. @package     jelix
  4. @subpackage  urls_engine
  5. @author      Laurent Jouanneau
  6. @contributor Thibault Piront (nuKs)
  7. @copyright   2005-2012 Laurent Jouanneau
  8. @copyright   2007 Thibault Piront
  9. @link        http://www.jelix.org
  10. @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html
  11. */
  12.  
  13. class significantUrlInfoParsing {
  14.     public $entryPoint = '';
  15.     public $entryPointUrl = '';
  16.     public $isHttps = false;
  17.     public $isDefault = false;
  18.     public $action = '';
  19.     public $module = '';
  20.     public $actionOverride = false;
  21.     public $requestType = '';
  22.     public $statics = array();
  23.     public $params = array();
  24.     public $escapes = array();
  25.  
  26.     function __construct($rt$ep$isDefault$isHttps{
  27.         $this->requestType = $rt;
  28.         $this->entryPoint = $this->entryPointUrl = $ep;
  29.         $this->isDefault = $isDefault;
  30.         $this->isHttps = $isHttps;
  31.     }
  32.  
  33.     function getFullSel({
  34.         if ($this->action{
  35.             $act $this->action;
  36.             if (substr($act,-1== ':'// this is a rest action
  37.                 // we should add index because jSelectorAct resolve a "ctrl:" as "ctrl:index"
  38.                 // and then create the corresponding selector so url create infos will be found
  39.                 $act .= 'index';
  40.         }
  41.         else
  42.             $act '*';
  43.         return $this->module.'~'.$act.'@'.$this->requestType;
  44.     }
  45. }
  46.  
  47. /**
  48. * Compiler for significant url engine
  49. @package  jelix
  50. @subpackage urls_engine
  51. */
  52. class jSignificantUrlsCompiler implements jISimpleCompiler{
  53.  
  54.     protected $requestType;
  55.     protected $defaultUrl;
  56.     protected $parseInfos;
  57.     protected $createUrlInfos;
  58.     protected $createUrlContent;
  59.     protected $checkHttps = true;
  60.  
  61.     protected $typeparam = array('string'=>'([^\/]+)','char'=>'([^\/])''letter'=>'(\w)',
  62.         'number'=>'(\d+)''int'=>'(\d+)''integer'=>'(\d+)''digit'=>'(\d)',
  63.         'date'=>'([0-2]\d{3}\-(?:0[1-9]|1[0-2])\-(?:[0-2][1-9]|3[0-1]))',
  64.         'year'=>'([0-2]\d{3})''month'=>'(0[1-9]|1[0-2])''day'=>'([0-2][1-9]|[1-2]0|3[0-1])',
  65.         'path'=>'(.*)''locale'=>'(\w{2,3}(?:(?:\-|_)\w{2,3})?)''lang'=>'(\w{2,3})'
  66.         );
  67.  
  68.     /**
  69.      *
  70.      */
  71.     public function compile($aSelector{
  72.  
  73.         $sourceFile $aSelector->getPath();
  74.         $cachefile $aSelector->getCompiledFilePath();
  75.  
  76.         $xml simplexml_load_file ($sourceFile);
  77.         if (!$xml{
  78.            return false;
  79.         }
  80.         /*
  81.         <urls>
  82.          <classicentrypoint name="index" default="true">
  83.             <url pathinfo="/test/:mois/:annee" module="" action="">
  84.                   <param name="mois" escape="true" regexp="\d{4}"/>
  85.                   <param name="annee" escape="false" />
  86.                   <static name="bla" value="cequejeveux" />
  87.             </url>
  88.             <url handler="" module="" action=""  />
  89.          </classicentrypoint>
  90.         </urls>
  91.  
  92.         The compiler generates two files.
  93.  
  94.         It generates a php file for each entrypoint. A file contains a $PARSE_URL
  95.         array:
  96.  
  97.             $PARSE_URL = array($isDefault, $infoparser, $infoparser, ... )
  98.  
  99.         where:
  100.             $isDefault: true if it is the default entry point. In this case and
  101.             where the url parser doesn't find a corresponding action, it will
  102.             ignore else it will generate an error
  103.  
  104.             $infoparser = array('module','action', 'regexp_pathinfo',
  105.                                 'handler selector', array('secondaries','actions'),
  106.                                 false // needs https or not
  107.                                 )
  108.             or
  109.             $infoparser = array('module','action','regexp_pathinfo',
  110.                array('year','month'), // list of dynamic value included in the url,
  111.                                       // alphabetical ascendant order
  112.                array(true, false),    // list of boolean which indicates for each
  113.                                       // dynamic value, if it is an escaped value or not
  114.                array('bla'=>'whatIWant' ), // list of static values
  115.                array('secondaries','actions'),
  116.                false  // need https or not
  117.             )
  118.  
  119.         It generates an other file common to all entry point. It contains an
  120.         array which contains informations to create urls
  121.  
  122.             $CREATE_URL = array(
  123.                'news~show@classic' => // the action selector
  124.                   array(0,'entrypoint', https true/false, 'handler selector')
  125.                   or
  126.                   array(1,'entrypoint', https true/false,
  127.                         array('year','month',), // list of dynamic values included in the url
  128.                         array(0, 1..), // list of integer which indicates for each
  129.                                        // dynamic value: 0: urlencode, 1:urlencode except '/', 2:escape, 4: lang, 8: locale
  130.                         "/news/%1/%2/", // the url
  131.                         array('bla'=>'whatIWant' ) // list of static values
  132.                         )
  133.                   or
  134.                   When there are  several urls to the same action, it is an array of this kind of the previous array:
  135.                   array(4, array(1,...), array(1,...)...)
  136.  
  137.                   or
  138.                   array(2,'entrypoint', https true/false), // for the patterns "@request"
  139.                   or
  140.                   array(3,'entrypoint', https true/false), // for the patterns "module~@request"
  141.         */
  142.  
  143.         $this->createUrlInfos = array();
  144.         $this->createUrlContent = "<?php \n";
  145.         $this->readProjectXml();
  146.         $this->retrieveModulePaths(jApp::configPath('defaultconfig.ini.php'));
  147.         // for an app on a simple http server behind an https proxy, we shouldn't check HTTPS
  148.         $this->checkHttps = jApp::config()->urlengine['checkHttpsOnParsing'];
  149.  
  150.         foreach ($xml->children(as $tagname => $tag{
  151.             if (!preg_match("/^(.*)entrypoint$/"$tagname$m)) {
  152.                 //TODO : error
  153.                 continue;
  154.             }
  155.             $type $m[1];
  156.             if ($type == ''{
  157.                 if (isset($tag['type']))
  158.                     $type = (string)$tag['type'];
  159.                 if ($type == '')
  160.                     $type 'classic';
  161.             }
  162.  
  163.             $this->defaultUrl = new significantUrlInfoParsing (
  164.                 $type,
  165.                 (string)$tag['name'],
  166.                 (isset($tag['default'](((string)$tag['default']== 'true'):false),
  167.                 (isset($tag['https'](((string)$tag['https']== 'true'):false)
  168.             );
  169.  
  170.             if (isset($tag['noentrypoint']&& (string)$tag['noentrypoint'== 'true')
  171.                 $this->defaultUrl->entryPointUrl '';
  172.  
  173.             $optionalTrailingSlash (isset($tag['optionalTrailingSlash']&& $tag['optionalTrailingSlash'== 'true');
  174.  
  175.             $this->parseInfos = array($this->defaultUrl->isDefault);
  176.  
  177.             //let's read the modulesPath of the entry point
  178.             $this->retrieveModulePaths($this->getEntryPointConfig($this->defaultUrl->entryPoint));
  179.  
  180.             // if this is the default entry point for the request type,
  181.             // then we add a rule which will match urls which are not
  182.             // defined.
  183.             if ($this->defaultUrl->isDefault{
  184.                 $this->createUrlInfos['@'.$this->defaultUrl->requestTypearray(2$this->defaultUrl->entryPoint$this->defaultUrl->isHttps);
  185.             }
  186.  
  187.             $createUrlInfosDedicatedModules array();
  188.             $parseContent "<?php \n";
  189.  
  190.             foreach ($tag->children(as $tagname => $url{
  191.                 $u clone $this->defaultUrl;
  192.                 $u->module = (string)$url['module'];
  193.  
  194.                 if (isset($url['https'])) {
  195.                     $u->isHttps (((string)$url['https']== 'true');
  196.                 }
  197.  
  198.                 if (isset($url['noentrypoint']&& ((string)$url['noentrypoint']== 'true'{
  199.                     $u->entryPointUrl '';
  200.                 }
  201.  
  202.                 if (isset($url['include'])) {
  203.                     $this->readInclude($url$u);
  204.                     continue;
  205.                 }
  206.  
  207.                 // in the case of a non default entry point, if there is just an
  208.                 // <url module="" />, so all actions of this module will be assigned
  209.                 // to this entry point.
  210.                 if (!$u->isDefault && !isset($url['action']&& !isset($url['handler'])) {
  211.                     $this->parseInfos[array($u->module'''/.*/'array(),
  212.                                                 array()array()false,
  213.                                                 ($this->checkHttps && $u->isHttps));
  214.                     $createUrlInfosDedicatedModules[$u->getFullSel()array(3$u->entryPointUrl$u->isHttpstrue);
  215.                     continue;
  216.                 }
  217.  
  218.                 $u->action = (string)$url['action'];
  219.  
  220.                 if (strpos($u->action':'=== false{
  221.                     $u->action 'default:'.$u->action;
  222.                 }
  223.  
  224.                 if (isset($url['actionoverride'])) {
  225.                     $u->actionOverride preg_split("/[\s,]+/"(string)$url['actionoverride']);
  226.                     foreach ($u->actionOverride as &$each{
  227.                         if (strpos($each':'=== false{
  228.                             $each 'default:'.$each;
  229.                         }
  230.                     }
  231.                 }
  232.  
  233.                 // if there is an indicated handler, so, for the given module
  234.                 // (and optional action), we should call the given handler to
  235.                 // parse or create an url
  236.                 if (isset($url['handler'])) {
  237.                     $this->newHandler($u$url);
  238.                     continue;
  239.                 }
  240.  
  241.                 // parse dynamic parameters
  242.                 if (isset($url['pathinfo'])) {
  243.                     $path = (string)$url['pathinfo'];
  244.                     $regexppath $this->extractDynamicParams($url$path$u);
  245.                 }
  246.                 else {
  247.                     $regexppath '.*';
  248.                     $path '';
  249.                 }
  250.  
  251.                 $tempOptionalTrailingSlash $optionalTrailingSlash;
  252.                 if (isset($url['optionalTrailingSlash'])) {
  253.                     $tempOptionalTrailingSlash ($url['optionalTrailingSlash'== 'true');
  254.                 }
  255.                 if ($tempOptionalTrailingSlash{
  256.                     if (substr($regexppath-1== '/'{
  257.                         $regexppath .= '?';
  258.                     }
  259.                     else {
  260.                         $regexppath .= '\/?';
  261.                     }
  262.                 }
  263.  
  264.                 // parse static parameters
  265.                 foreach ($url->static as $var{
  266.                     $t "";
  267.                     if (isset($var['type'])) {
  268.                         switch ((string) $var['type']{
  269.                             case 'lang'$t '$l'break;
  270.                             case 'locale'$t '$L'break;
  271.                             default:
  272.                                 throw new Exception('urls definition: invalid type on a <static> element');
  273.                         }
  274.                     }
  275.                     $u->statics[(string)$var['name']] $t . (string)$var['value'];
  276.                 }
  277.  
  278.                 $this->parseInfos[array($u->module$u->action'!^'.$regexppath.'$!',
  279.                                             $u->params$u->escapes$u->statics,
  280.                                             $u->actionOverride($this->checkHttps && $u->isHttps));
  281.                 $this->appendUrlInfo($u$pathfalse);
  282.  
  283.                 if ($u->actionOverride{
  284.                     foreach ($u->actionOverride as $ao{
  285.                         $u->action $ao;
  286.                         $this->appendUrlInfo($u$pathtrue);
  287.                     }
  288.                 }
  289.             }
  290.             $c count($createUrlInfosDedicatedModules);
  291.             foreach ($createUrlInfosDedicatedModules as $k=>$inf{
  292.                 if ($c 1)
  293.                     $inf[3false;
  294.                 $this->createUrlInfos[$k$inf;
  295.             }
  296.  
  297.             $parseContent .= '$GLOBALS[\'SIGNIFICANT_PARSEURL\'][\''.rawurlencode($this->defaultUrl->entryPoint).'\'] = '
  298.                             .var_export($this->parseInfostrue).";\n?>";
  299.  
  300.             jFile::write(jApp::tempPath('compiled/urlsig/'.$aSelector->file.'.'.rawurlencode($this->defaultUrl->entryPoint).'.entrypoint.php'),$parseContent);
  301.         }
  302.         $this->createUrlContent .= '$GLOBALS[\'SIGNIFICANT_CREATEURL\'] ='.var_export($this->createUrlInfostrue).";\n?>";
  303.         jFile::write(jApp::tempPath('compiled/urlsig/'.$aSelector->file.'.creationinfos.php')$this->createUrlContent);
  304.         return true;
  305.     }
  306.  
  307.     protected function readProjectXml({
  308.         $xml simplexml_load_file(jApp::appPath('project.xml'));
  309.         foreach ($xml->entrypoints->entry as $entrypoint{
  310.             $file = (string)$entrypoint['file'];
  311.             if (substr($file-4!= '.php')
  312.                 $file.='.php';
  313.             $configFile = (string)$entrypoint['config'];
  314.             $this->entryPoints[$file$configFile;
  315.         }
  316.     }
  317.  
  318.     protected function getEntryPointConfig($entrypoint{
  319.         if (substr($entrypoint-4!= '.php')
  320.             $entrypoint.='.php';
  321.         if (!isset($this->entryPoints[$entrypoint]))
  322.             throw new Exception('The entry point "'.$entrypoint.'" is not declared into project.xml');
  323.         return jApp::configPath($this->entryPoints[$entrypoint]);
  324.     }
  325.     /**
  326.      * list all entry points and their config
  327.      */
  328.     protected $entryPoints = array();
  329.  
  330.     /**
  331.      * list all modules repository
  332.      */
  333.     protected $modulesRepositories = array();
  334.  
  335.     /**
  336.      * list all modules path
  337.      */
  338.     protected $modulesPath = array();
  339.  
  340.     /**
  341.      * since urls.xml declare all entrypoints, current entry point does not have
  342.      * access to all modules, so doesn't know all their paths.
  343.      * this method retrieve all module paths declared in the configuration
  344.      * of an entry point or the global configuration
  345.      * @param string $configFile the config file name
  346.      */
  347.     protected function retrieveModulePaths($configFile{
  348.         $conf parse_ini_file($configFile);
  349.         if (!array_key_exists('modulesPath',$conf))
  350.             return;
  351.         $list preg_split('/ *, */',$conf['modulesPath']);
  352.         array_unshift($listJELIX_LIB_PATH.'core-modules/');
  353.  
  354.         foreach($list as $k=>$path){
  355.             if(trim($path== ''continue;
  356.             $p str_replace(array('lib:','app:')array(LIB_PATHjApp::appPath())$path);
  357.             if (!file_exists($p)) {
  358.                 continue;
  359.             }
  360.             if (substr($p,-1!='/')
  361.                 $p.='/';
  362.             if (isset($this->modulesRepositories[$p]))
  363.                 continue;
  364.             $this->modulesRepositories[$ptrue;
  365.             if ($handle opendir($p)) {
  366.                 while (false !== ($f readdir($handle))) {
  367.                     if ($f[0!= '.' && is_dir($p.$f)) {
  368.                         $this->modulesPath[$f]=$p.$f.'/';
  369.                     }
  370.                 }
  371.                 closedir($handle);
  372.             }
  373.         }
  374.     }
  375.  
  376.     /**
  377.      * @param significantUrlInfoParsing $u 
  378.      * @param simpleXmlElement $url 
  379.     */
  380.     protected function newHandler($u$url$pathinfo ''{
  381.         $class = (string)$url['handler'];
  382.         // we must have a module name in the selector, because, during the parsing of
  383.         // the url in the request process, we are not still in a module context
  384.         $p strpos($class,'~');
  385.         if ($p === false)
  386.             $selclass $u->module.'~'.$class;
  387.         elseif ($p == 0)
  388.             $selclass $u->module.$class;
  389.         else
  390.             $selclass $class;
  391.  
  392.         $s new jSelectorUrlHandler($selclass);
  393.         if (!isset($url['action'])) {
  394.             $u->action '*';
  395.         }
  396.         $regexp '';
  397.  
  398.         if (isset($url['pathinfo'])) {
  399.             $pathinfo .= '/'.trim((string)$url['pathinfo']'/');
  400.         }
  401.  
  402.         if ($pathinfo != '/'{
  403.             $regexp '!^'.preg_quote($pathinfo'!').'(/.*)?$!';
  404.         }
  405.  
  406.         $this->createUrlContent .= "include_once('".$s->getPath()."');\n";
  407.         $this->parseInfos[array($u->module$u->action$regexp$selclass$u->actionOverride($this->checkHttps && $u->isHttps));
  408.         $this->createUrlInfos[$u->getFullSel()array(0$u->entryPointUrl$u->isHttps$selclass$pathinfo);
  409.         if ($u->actionOverride{
  410.             foreach ($u->actionOverride as $ao{
  411.                 $u->action $ao;
  412.                 $this->createUrlInfos[$u->getFullSel()array(0$u->entryPointUrl$u->isHttps$selclass$pathinfo);
  413.             }
  414.         }
  415.     }
  416.  
  417.     /**
  418.      * extract all dynamic parts of a pathinfo, and read <param> elements
  419.      * @param simpleXmlElement $url the url element
  420.      * @param string $regexppath  the path info
  421.      * @param significantUrlInfoParsing $u 
  422.      * @return string the correponding regular expression
  423.      */
  424.     protected function extractDynamicParams($url$regexppath$u{
  425.         $regexppath preg_quote($regexppath '!');
  426.         if (preg_match_all("/\\\:([a-zA-Z_0-9]+)/"$regexppath$mPREG_PATTERN_ORDER)) {
  427.             $u->params $m[1];
  428.  
  429.             // process parameters which are declared in a <param> element
  430.             foreach ($url->param as $var{
  431.  
  432.                 $name = (string) $var['name'];
  433.                 $k array_search($name$u->params);
  434.                 if ($k === false{
  435.                     // TODO error
  436.                     continue;
  437.                 }
  438.  
  439.                 $type '';
  440.                 if (isset($var['type'])) {
  441.                     $type = (string)$var['type'];
  442.                     if (isset($this->typeparam[$type]))
  443.                         $regexp $this->typeparam[$type];
  444.                     else
  445.                         $regexp '([^\/]+)';
  446.                 }
  447.                 elseif (isset ($var['regexp'])) {
  448.                     $regexp '('.(string)$var['regexp'].')';
  449.                 }
  450.                 else {
  451.                     $regexp '([^\/]+)';
  452.                 }
  453.  
  454.                 $u->escapes[$k0;
  455.                 if ($type == 'path'{
  456.                     $u->escapes[$k1;
  457.                 }
  458.                 else if (isset($var['escape'])) {
  459.                     $u->escapes[$k(((string)$var['escape']== 'true'?2:0);
  460.                 }
  461.  
  462.                 if ($type == 'lang'{
  463.                     $u->escapes[$k|= 4;
  464.                 }
  465.                 else if ($type == 'locale'{
  466.                     $u->escapes[$k|= 8;
  467.                 }
  468.  
  469.                 $regexppath str_replace('\:'.$name$regexp$regexppath);
  470.             }
  471.  
  472.             // process parameters that are only declared in the pathinfo
  473.             foreach ($u->params as $k=>$name{
  474.                 if (isset($u->escapes[$k])) {
  475.                     continue;
  476.                 }
  477.                 $u->escapes[$k0;
  478.                 $regexppath str_replace('\:'.$name'([^\/]+)'$regexppath);
  479.             }
  480.         }
  481.         return $regexppath;
  482.     }
  483.  
  484.     /**
  485.      * register the given url informations
  486.      * @param significantUrlInfoParsing $u 
  487.      * @param string $path 
  488.      */
  489.     protected function appendUrlInfo($u$path$secondaryAction{
  490.         $cuisel $u->getFullSel();
  491.         $arr array(1$u->entryPointUrl$u->isHttps$u->params$u->escapes$path$secondaryAction$u->statics);
  492.         if (isset($this->createUrlInfos[$cuisel])) {
  493.             if ($this->createUrlInfos[$cuisel][0== 4{
  494.                 $this->createUrlInfos[$cuisel][$arr;
  495.             }
  496.             else {
  497.                 $this->createUrlInfos[$cuiselarray(4$this->createUrlInfos[$cuisel]$arr);
  498.             }
  499.         }
  500.         else {
  501.             $this->createUrlInfos[$cuisel$arr;
  502.         }
  503.     }
  504.  
  505.     /**
  506.      * @param simpleXmlElement $url 
  507.      * @param significantUrlInfoParsing $uInfo 
  508.     */
  509.     protected function readInclude($url$uInfo{
  510.  
  511.         $file = (string)$url['include'];
  512.         $pathinfo '/'.trim((string)$url['pathinfo']'/');
  513.  
  514.         if (!isset($this->modulesPath[$uInfo->module]))
  515.             throw new Exception ('urls.xml: the module '.$uInfo->module.' does not exist');
  516.  
  517.         $path $this->modulesPath[$uInfo->module];
  518.  
  519.         if (!file_exists($path.$file))
  520.             throw new Exception ('urls.xml: include file '.$file.' of the module '.$uInfo->module.' does not exist');
  521.  
  522.         $xml simplexml_load_file ($path.$file);
  523.         if (!$xml{
  524.            throw new Exception ('urls.xml: include file '.$file.' of the module '.$uInfo->module.' is not a valid xml file');
  525.         }
  526.         $optionalTrailingSlash (isset($xml['optionalTrailingSlash']&& $xml['optionalTrailingSlash'== 'true');
  527.  
  528.         foreach ($xml->url as $url{
  529.             $u clone $uInfo;
  530.  
  531.             $u->action = (string)$url['action'];
  532.  
  533.             if (strpos($u->action':'=== false{
  534.                 $u->action 'default:'.$u->action;
  535.             }
  536.  
  537.             if (isset($url['actionoverride'])) {
  538.                 $u->actionOverride preg_split("/[\s,]+/"(string)$url['actionoverride']);
  539.                 foreach ($u->actionOverride as &$each{
  540.                     if (strpos($each':'=== false{
  541.                         $each 'default:'.$each;
  542.                     }
  543.                 }
  544.             }
  545.  
  546.             // if there is an indicated handler, so, for the given module
  547.             // (and optional action), we should call the given handler to
  548.             // parse or create an url
  549.             if (isset($url['handler'])) {
  550.                 $this->newHandler($u$url$pathinfo);
  551.                 continue;
  552.             }
  553.  
  554.             // parse dynamic parameters
  555.             if (isset($url['pathinfo'])) {
  556.                 $path $pathinfo.($pathinfo !='/'?'/':'').trim((string)$url['pathinfo'],'/');
  557.                 $regexppath $this->extractDynamicParams($url$path$u);
  558.             }
  559.             else {
  560.                 $regexppath '.*';
  561.                 $path '';
  562.             }
  563.  
  564.             $tempOptionalTrailingSlash $optionalTrailingSlash;
  565.             if (isset($url['optionalTrailingSlash'])) {
  566.                 $tempOptionalTrailingSlash ($url['optionalTrailingSlash'== 'true');
  567.             }
  568.             if ($tempOptionalTrailingSlash{
  569.                 if (substr($regexppath-1== '/'{
  570.                     $regexppath .= '?';
  571.                 }
  572.                 else {
  573.                     $regexppath .= '\/?';
  574.                 }
  575.             }
  576.  
  577.             // parse static parameters
  578.             foreach ($url->static as $var{
  579.                 $t "";
  580.                 if (isset($var['type'])) {
  581.                     switch ((string) $var['type']{
  582.                         case 'lang'$t '$l'break;
  583.                         case 'locale'$t '$L'break;
  584.                         default:
  585.                             throw new Exception('urls definition: invalid type on a <static> element');
  586.                     }
  587.                 }
  588.                 $u->statics[(string)$var['name']] $t . (string)$var['value'];
  589.             }
  590.  
  591.             $this->parseInfos[array($u->module$u->action'!^'.$regexppath.'$!',
  592.                                         $u->params$u->escapes$u->statics,
  593.                                         $u->actionOverride($this->checkHttps && $u->isHttps));
  594.             $this->appendUrlInfo($u$pathfalse);
  595.             if ($u->actionOverride{
  596.                 foreach ($u->actionOverride as $ao{
  597.                     $u->action $ao;
  598.                     $this->appendUrlInfo($u$pathtrue);
  599.                 }
  600.             }
  601.         }
  602.     }
  603.  
  604. }

Documentation generated on Mon, 26 Oct 2015 21:56:23 +0100 by phpDocumentor 1.4.3