Summary: | [Chameleon] Recursive preprocessing enhancement | ||
---|---|---|---|
Product: | Chameleon | Reporter: | Julien-Samuel Lacroix <jlacroix@mapgears.com> |
Component: | Core | Assignee: | chameleon-dev <chameleon-dev@lists.maptools.org> |
Status: | NEW | ||
Severity: | enhancement | ||
Priority: | P2 | ||
Version: | 1.99 | ||
Target Milestone: | FUTURE | ||
Hardware: | PC | ||
OS: | Linux | ||
Whiteboard: |
Suggestion from Paul: Julien, sorry, I didn't really follow this thread too closely so I am not entirely sure what the patch does ... here is my interpretation: 1. process [$$] replacements << added by pxniw 2. process [##] includes 3. process [$$] replacements if this is right, then I have some comments ... * if we are allowing multiple passes, should we run the include process twice (i.e. pages included using [##] could include [##] inside them? * simply adding more steps isn't going to solve the recursive cases ... * there is a lot of code duplication I suggest a more robust implementation that takes the code and divides it into two functions /** * Replace text in target by searching for blocks delimited by the start * and end delimiters. The caller provides a method that takes a text * string and returns the value to be inserted in place of the tag, or * false if no tag replacement is possible. This method returns the * number of replacements that actually happened. * * A note on fReplacement. It can be a string (name of function to * call) or an array. If it is an array, it must contain 2 elements, * the first element is the name of the function to call. The second * element is either the name of a class, in which case the function * is called as a static method of the class using call_user_func, or * it is an object and is called using call_user_method. * * @param fReplacer a function that returns the text to be inserted * @param szText the text to be scanned for replacements * @param szStart a text string that defines the start of a replacement * block * @param szEnd a text string that defines the end of a replacement * block * @return the number of replacements that happened. */ ProcessReplacements( $fReplacement, $szText, $szStart = "[$", $szEnd = "$]" ) { $nReplacements = 0; ... search for start and end tags, assign to $szKey ... in loop //start loop { if (is_array($fReplacement) && isset($fReplacement[1])) { if (is_object($fReplacement[1])) { //use call_user_method $szReplacement = call_user_method($fReplacement[0], $fReplacement[1], $szKey ); } else { //use call_user_func, assume that array is in $szReplacement = call_user_func( $fReplacement, $szKey ); } else { //this would be an error } } else { //use call_user_func, assume that array is in $szReplacement = call_user_func( $fReplacement, $szKey ); } if ($szReplacment !=== false) { //process replacement now $nReplacements ++; } else { //leave text alone, no replacement to happen. } //end loop return $nReplacements; } Then in the TemplateParser code, we would use something like the following block $nReplacements = 1; //just to get us into the loop $nLevel = 10; while ($nReplacements > 0 && $nLevel > 0) { $nReplacements = 0; $nReplacements = $this->ProcessReplacements( array( "GetReplacement", $this ), $this->mszTemplate ); "[$", "$]" ); $nReplacements += $this->ProcessReplacements( array( "GetInclude", $this ), $this->mszTemplate, "[#", "#]" ); $nLevel ++; } And we would add the following methods to the TemplateParser /* * return a value from the URL using the passed value as the key. * If the key is not present, return false, otherwise return the * value. */ GetReplacement( $szTextToReplace ) { .... } /* * return the contents of a file based on the file name passed. * If the file does not exist, return false, otherwise return the * contents of the file. */ GetInclude( $szFileToInclude ) { .... } Cheers, Paul
To avoid to much performance hit we can also do a linear parsing. I mean do the replacement and in the include step, take the file to include and do the replacement on only its content, not the whole Chameleon template 2-3 times. What do you think? function ProcessReplacement($szTextToReplace) { $szTextToReplace = preg_replace_callback("/(\\[$(.*?)$\\])/", "GetReplacement", $szTextToReplace); $szTextToReplace = preg_replace_callback("/(\\[#(.*?)#\\])/", "GetInclude", $szTextToReplace); return $szTextToReplace; } GetReplacement( $szTextToReplace ) { .... } GetInclude( $aszTextToReplace ) { // Get the file to include $szTemplateName = $aszTextToReplace[2]; $szTemplate = implode("\n", file($szTemplateName)); // Reprocess only the file to include $szTemplate = ProcessReplacement($szTemplate); return }
yes I agree. So in the ProcessReplacement function, for each result coming from an include, you would actually call ProcessReplacement on the result before pasting it into the original text? This could require some changes to the approach I suggested.
Wow it's nice to see how short my thought can be summarized. However we won't be able to do things like: [$[#file/to/[$include$].txt#]$] But I think this is OK. We should not let users do things like that. ;)
I think this is a reasonable limitation :)
Changed target to 1.99 Final. Paul, is it reasonable to get this done for 1.99?
Changing Version to 1.99 (2.0 not being worked on yet).
Changed Severity to normal.
future enhancement