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

Documentation generated on Thu, 19 Sep 2013 00:07:31 +0200 by phpDocumentor 1.4.3