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-2011 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'=>'(.*)'
  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(true, false..), // list of integer which indicates for each
  129.                                               // dynamic value: 0: urlencode, 1:urlencode except '/', 2:escape
  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 = $GLOBALS['gJConfig']->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.                     $u->statics[(string)$var['name']] = (string)$var['value'];
  267.                 }
  268.  
  269.                 $this->parseInfos[array($u->module$u->action'!^'.$regexppath.'$!',
  270.                                             $u->params$u->escapes$u->statics,
  271.                                             $u->actionOverride($this->checkHttps && $u->isHttps));
  272.                 $this->appendUrlInfo($u$pathfalse);
  273.  
  274.                 if ($u->actionOverride{
  275.                     foreach ($u->actionOverride as $ao{
  276.                         $u->action $ao;
  277.                         $this->appendUrlInfo($u$pathtrue);
  278.                     }
  279.                 }
  280.             }
  281.             $c count($createUrlInfosDedicatedModules);
  282.             foreach ($createUrlInfosDedicatedModules as $k=>$inf{
  283.                 if ($c 1)
  284.                     $inf[3false;
  285.                 $this->createUrlInfos[$k$inf;
  286.             }
  287.  
  288.             $parseContent .= '$GLOBALS[\'SIGNIFICANT_PARSEURL\'][\''.rawurlencode($this->defaultUrl->entryPoint).'\'] = '
  289.                             .var_export($this->parseInfostrue).";\n?>";
  290.  
  291.             jFile::write(jApp::tempPath('compiled/urlsig/'.$aSelector->file.'.'.rawurlencode($this->defaultUrl->entryPoint).'.entrypoint.php'),$parseContent);
  292.         }
  293.         $this->createUrlContent .= '$GLOBALS[\'SIGNIFICANT_CREATEURL\'] ='.var_export($this->createUrlInfostrue).";\n?>";
  294.         jFile::write(jApp::tempPath('compiled/urlsig/'.$aSelector->file.'.creationinfos.php')$this->createUrlContent);
  295.         return true;
  296.     }
  297.  
  298.     protected function readProjectXml({
  299.         $xml simplexml_load_file(jApp::appPath('project.xml'));
  300.         foreach ($xml->entrypoints->entry as $entrypoint{
  301.             $file = (string)$entrypoint['file'];
  302.             if (substr($file-4!= '.php')
  303.                 $file.='.php';
  304.             $configFile = (string)$entrypoint['config'];
  305.             $this->entryPoints[$file$configFile;
  306.         }
  307.     }
  308.  
  309.     protected function getEntryPointConfig($entrypoint{
  310.         if (substr($entrypoint-4!= '.php')
  311.             $entrypoint.='.php';
  312.         if (!isset($this->entryPoints[$entrypoint]))
  313.             throw new Exception('The entry point "'.$entrypoint.'" is not declared into project.xml');
  314.         return jApp::configPath($this->entryPoints[$entrypoint]);
  315.     }
  316.     /**
  317.      * list all entry points and their config
  318.      */
  319.     protected $entryPoints = array();
  320.  
  321.     /**
  322.      * list all modules repository
  323.      */
  324.     protected $modulesRepositories = array();
  325.  
  326.     /**
  327.      * list all modules path
  328.      */
  329.     protected $modulesPath = array();
  330.  
  331.     /**
  332.      * since urls.xml declare all entrypoints, current entry point does not have
  333.      * access to all modules, so doesn't know all their paths.
  334.      * this method retrieve all module paths declared in the configuration
  335.      * of an entry point or the global configuration
  336.      * @param string $configFile the config file name
  337.      */
  338.     protected function retrieveModulePaths($configFile{
  339.         $conf parse_ini_file($configFile);
  340.         if (!array_key_exists('modulesPath',$conf))
  341.             return;
  342.         $list preg_split('/ *, */',$conf['modulesPath']);
  343.         array_unshift($listJELIX_LIB_PATH.'core-modules/');
  344.  
  345.         foreach($list as $k=>$path){
  346.             if(trim($path== ''continue;
  347.             $p str_replace(array('lib:','app:')array(LIB_PATHjApp::appPath())$path);
  348.             if (!file_exists($p)) {
  349.                 continue;
  350.             }
  351.             if (substr($p,-1!='/')
  352.                 $p.='/';
  353.             if (isset($this->modulesRepositories[$p]))
  354.                 continue;
  355.             $this->modulesRepositories[$ptrue;
  356.             if ($handle opendir($p)) {
  357.                 while (false !== ($f readdir($handle))) {
  358.                     if ($f[0!= '.' && is_dir($p.$f)) {
  359.                         $this->modulesPath[$f]=$p.$f.'/';
  360.                     }
  361.                 }
  362.                 closedir($handle);
  363.             }
  364.         }
  365.     }
  366.  
  367.     /**
  368.      * @param significantUrlInfoParsing $u 
  369.      * @param simpleXmlElement $url 
  370.     */
  371.     protected function newHandler($u$url$pathinfo ''{
  372.         $class = (string)$url['handler'];
  373.         // we must have a module name in the selector, because, during the parsing of
  374.         // the url in the request process, we are not still in a module context
  375.         $p strpos($class,'~');
  376.         if ($p === false)
  377.             $selclass $u->module.'~'.$class;
  378.         elseif ($p == 0)
  379.             $selclass $u->module.$class;
  380.         else
  381.             $selclass $class;
  382.  
  383.         $s new jSelectorUrlHandler($selclass);
  384.         if (!isset($url['action'])) {
  385.             $u->action '*';
  386.         }
  387.         $regexp '';
  388.  
  389.         if (isset($url['pathinfo'])) {
  390.             $pathinfo .= '/'.trim((string)$url['pathinfo']'/');
  391.         }
  392.  
  393.         if ($pathinfo != '/'{
  394.             $regexp '!^'.preg_quote($pathinfo'!').'(/.*)?$!';
  395.         }
  396.  
  397.         $this->createUrlContent .= "include_once('".$s->getPath()."');\n";
  398.         $this->parseInfos[array($u->module$u->action$regexp$selclass$u->actionOverride($this->checkHttps && $u->isHttps));
  399.         $this->createUrlInfos[$u->getFullSel()array(0$u->entryPointUrl$u->isHttps$selclass$pathinfo);
  400.         if ($u->actionOverride{
  401.             foreach ($u->actionOverride as $ao{
  402.                 $u->action $ao;
  403.                 $this->createUrlInfos[$u->getFullSel()array(0$u->entryPointUrl$u->isHttps$selclass$pathinfo);
  404.             }
  405.         }
  406.     }
  407.  
  408.     /**
  409.      * extract all dynamic parts of a pathinfo
  410.      * @param simpleXmlElement $url the url element
  411.      * @param string $regexppath  the path info
  412.      * @param significantUrlInfoParsing $u 
  413.      * @return string the correponding regular expression
  414.      */
  415.     protected function extractDynamicParams($url$regexppath$u{
  416.         $regexppath preg_quote($regexppath '!');
  417.         if (preg_match_all("/\\\:([a-zA-Z_]+)/"$regexppath$mPREG_PATTERN_ORDER)) {
  418.             $u->params $m[1];
  419.  
  420.             // process parameters which are declared in a <param> element
  421.             foreach ($url->param as $var{
  422.  
  423.                 $name = (string) $var['name'];
  424.                 $k array_search($name$u->params);
  425.                 if ($k === false{
  426.                     // TODO error
  427.                     continue;
  428.                 }
  429.  
  430.                 $type '';
  431.                 if (isset($var['type'])) {
  432.                     $type = (string)$var['type'];
  433.                     if (isset($this->typeparam[$type]))
  434.                         $regexp $this->typeparam[$type];
  435.                     else
  436.                         $regexp '([^\/]+)';
  437.                 }
  438.                 elseif (isset ($var['regexp'])) {
  439.                     $regexp '('.(string)$var['regexp'].')';
  440.                 }
  441.                 else {
  442.                     $regexp '([^\/]+)';
  443.                 }
  444.  
  445.                 if ($type == 'path'{
  446.                     $u->escapes[$k1;
  447.                 }
  448.                 else if (isset($var['escape'])) {
  449.                     $u->escapes[$k(((string)$var['escape']== 'true'?2:0);
  450.                 }
  451.                 else {
  452.                     $u->escapes[$k0;
  453.                 }
  454.  
  455.                 $regexppath str_replace('\:'.$name$regexp$regexppath);
  456.             }
  457.  
  458.             // process parameters that are only declared in the pathinfo
  459.             foreach ($u->params as $k=>$name{
  460.                 if (isset($u->escapes[$k])) {
  461.                     continue;
  462.                 }
  463.                 $u->escapes[$k0;
  464.                 $regexppath str_replace('\:'.$name'([^\/]+)'$regexppath);
  465.             }
  466.         }
  467.         return $regexppath;
  468.     }
  469.  
  470.     /**
  471.      * register the given url informations
  472.      * @param significantUrlInfoParsing $u 
  473.      * @param string $path 
  474.      */
  475.     protected function appendUrlInfo($u$path$secondaryAction{
  476.         $cuisel $u->getFullSel();
  477.         $arr array(1$u->entryPointUrl$u->isHttps$u->params$u->escapes$path$secondaryAction$u->statics);
  478.         if (isset($this->createUrlInfos[$cuisel])) {
  479.             if ($this->createUrlInfos[$cuisel][0== 4{
  480.                 $this->createUrlInfos[$cuisel][$arr;
  481.             }
  482.             else {
  483.                 $this->createUrlInfos[$cuiselarray(4$this->createUrlInfos[$cuisel]$arr);
  484.             }
  485.         }
  486.         else {
  487.             $this->createUrlInfos[$cuisel$arr;
  488.         }
  489.     }
  490.  
  491.     /**
  492.      * @param simpleXmlElement $url 
  493.      * @param significantUrlInfoParsing $uInfo 
  494.     */
  495.     protected function readInclude($url$uInfo{
  496.  
  497.         $file = (string)$url['include'];
  498.         $pathinfo '/'.trim((string)$url['pathinfo']'/');
  499.  
  500.         if (!isset($this->modulesPath[$uInfo->module]))
  501.             throw new Exception ('urls.xml: the module '.$uInfo->module.' does not exist');
  502.  
  503.         $path $this->modulesPath[$uInfo->module];
  504.  
  505.         if (!file_exists($path.$file))
  506.             throw new Exception ('urls.xml: include file '.$file.' of the module '.$uInfo->module.' does not exist');
  507.  
  508.         $xml simplexml_load_file ($path.$file);
  509.         if (!$xml{
  510.            throw new Exception ('urls.xml: include file '.$file.' of the module '.$uInfo->module.' is not a valid xml file');
  511.         }
  512.         $optionalTrailingSlash (isset($xml['optionalTrailingSlash']&& $xml['optionalTrailingSlash'== 'true');
  513.  
  514.         foreach ($xml->url as $url{
  515.             $u clone $uInfo;
  516.  
  517.             $u->action = (string)$url['action'];
  518.  
  519.             if (strpos($u->action':'=== false{
  520.                 $u->action 'default:'.$u->action;
  521.             }
  522.  
  523.             if (isset($url['actionoverride'])) {
  524.                 $u->actionOverride preg_split("/[\s,]+/"(string)$url['actionoverride']);
  525.                 foreach ($u->actionOverride as &$each{
  526.                     if (strpos($each':'=== false{
  527.                         $each 'default:'.$each;
  528.                     }
  529.                 }
  530.             }
  531.  
  532.             // if there is an indicated handler, so, for the given module
  533.             // (and optional action), we should call the given handler to
  534.             // parse or create an url
  535.             if (isset($url['handler'])) {
  536.                 $this->newHandler($u$url$pathinfo);
  537.                 continue;
  538.             }
  539.  
  540.             // parse dynamic parameters
  541.             if (isset($url['pathinfo'])) {
  542.                 $path $pathinfo.($pathinfo !='/'?'/':'').trim((string)$url['pathinfo'],'/');
  543.                 $regexppath $this->extractDynamicParams($url$path$u);
  544.             }
  545.             else {
  546.                 $regexppath '.*';
  547.                 $path '';
  548.             }
  549.  
  550.             $tempOptionalTrailingSlash $optionalTrailingSlash;
  551.             if (isset($url['optionalTrailingSlash'])) {
  552.                 $tempOptionalTrailingSlash ($url['optionalTrailingSlash'== 'true');
  553.             }
  554.             if ($tempOptionalTrailingSlash{
  555.                 if (substr($regexppath-1== '/'{
  556.                     $regexppath .= '?';
  557.                 }
  558.                 else {
  559.                     $regexppath .= '\/?';
  560.                 }
  561.             }
  562.  
  563.             // parse static parameters
  564.             foreach ($url->static as $var{
  565.                 $u->statics[(string)$var['name']] = (string)$var['value'];
  566.             }
  567.  
  568.             $this->parseInfos[array($u->module$u->action'!^'.$regexppath.'$!',
  569.                                         $u->params$u->escapes$u->statics,
  570.                                         $u->actionOverride($this->checkHttps && $u->isHttps));
  571.             $this->appendUrlInfo($u$pathfalse);
  572.             if ($u->actionOverride{
  573.                 foreach ($u->actionOverride as $ao{
  574.                     $u->action $ao;
  575.                     $this->appendUrlInfo($u$pathtrue);
  576.                 }
  577.             }
  578.         }
  579.     }
  580.  
  581. }

Documentation generated on Wed, 24 Sep 2014 22:02:16 +0200 by phpDocumentor 1.4.3