Re[2]: [SMARTY-DEV] Incorrect newline counters

From: Date: Tue, 01 Jun 2004 15:35:24 +0000
Subject: Re[2]: [SMARTY-DEV] Incorrect newline counters
References: 1 2 3 4  Groups: php.smarty.dev 
Request: Send a blank email to php-smarty-dev+get-2354@lists.php.net to get a copy of this message
MO> The important thing here is that the template source matches MO> _exactly_ to the template output, which includes newlines. MO> This may not be so important with HTML output, but things MO> like e-mail templates it is crucial. Oh. First serious argument, wow. MO> PHP's behavior is to eat newline characters after ?> tags. MO> This is fine for your typical imbedded HTML programs, but for MO> template engines like Smarty it causes problems. Therefore to MO> make template output identical to template source, a lot of MO> careful work was done to add newline characters after ?> I'd like to write up Larry Wall's (I agreed with him) opinion about all this situation. It seems to me that we need to automatize this, but not insert \n's manually. Computers is made to make all jogtrot work, to free programmer from it. For programmer the word "carefull" in most cases means "wrong designed". MO> tags to essentially "undo" this PHP feature. When you say you MO> remove unnecessary \n's, this leads me to think it will break MO> output, meaning it will butt lines together that really need MO> to be separate. Have you tested it? I have spent half an our and now it is OK. See full description below. MO> As for line number counting in the compiled PHP files, I MO> don't see how that can or should correspond to line numbers MO> in the template source. If we can figure out a way to make MO> the line numbers correspond _and_ make template output MO> identical to source, then I don't see a problem with it. But No problem, here is the patch (attached). Idea of method: - In each text block we insert: $text = preg_replace("/^([\r\n]+)/s", '<?php echo "$1"; ?>', $text); - In code blocks we insert: $text = preg_replace("/([\r\n]+)/s", '<?php $1?>', $text); That's almost all. No need of $_additional_newline. I have a little question. Let's consider piece of code (t_controller does not modify or trim its body): {t_controller src="Orphus_Test" name="date" format="Y-m-d"} {$date} {/t_controller} <hr> Please say which result should it THEORETICALLY output? 1. "2004-06-01\n\n<hr>" or 2. "2004-06-01\n<hr>" Which one? ------------------------------------------------------- Roots (history) of the problem PHP ignores \n's after ?>: <?php echo "hello"?> world Result will be "helloworld" (no \n). In contrast of PHP, Smarty DOES insert \n's after constructions if they were there in the template: {"hello"} world Result will be: "hello\nworld" (\n is saved). My previous patch had not considered this situation, so Smarty's constructions began to work like PHP constructions (stripping \n's after "}"). Today I have totally (hope) fixed this. So, output of original Smarty and output of patched version is IDENTICALL, in all cases. But line numbers synchronization is now OK: compiled code matches source template line-by-line too. Main goal - giving error message with line number (E_NOTICE, for example) while executing the template you my directly watch into the SOURCE template and search for given line number in it. -- Best regards, Dmitry Koteroff.

--- Distrib.orig/libs/Smarty_Compiler.class.php Tue Feb 17 19:55:24 2004 +++ Distrib/libs/Smarty_Compiler.class.php Tue Jun 01 19:22:19 2004 @@ -255,8 +255,9 @@ . "'" , $source_content); - + /* Gather all template tags. */ preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match); $template_tags = $_match[1]; + $template_tags_sp = $_match[0]; /* Split content by template tags to obtain non-template content. */ $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content); @@ -276,5 +277,5 @@ if ($this->php_handling == SMARTY_PHP_PASSTHRU) { /* echo php contents */ - $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]); + $text_blocks[$curr_tb] = preg_replace('/%%%SMARTYSP'.$curr_sp.'%%%([\r\n]*)/s', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'."\1"; ?>', $text_blocks[$curr_tb]); } else if ($this->php_handling == SMARTY_PHP_QUOTE) { /* quote php tags */ @@ -285,5 +286,5 @@ } else { /* SMARTY_PHP_ALLOW, but echo non php starting tags */ - $sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]); + $sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))([\r\n]*)%i', '<?php echo \'\\1\'."$3"?>', $sp_match[1][$curr_sp]); $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]); } @@ -296,6 +297,13 @@ for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) { $this->_current_line_no += substr_count($text_blocks[$i], "\n"); + if ($this->_strip_depth || 0) { + $text_blocks[$i] = preg_replace("/[\t ]+(?=\r?\n)|(?<=\n)[\t ]+/s", '', $text_blocks[$i]); + $text_blocks[$i] = preg_replace("/([\r\n]+)/s", '<?php $1?'.'>', $text_blocks[$i]); + } $compiled_tags[] = $this->_compile_tag($template_tags[$i]); $this->_current_line_no += substr_count($template_tags[$i], "\n"); + if ($template_tags[$i] == "strip" || $template_tags[$i] == "/strip") { + $compiled_tags[count($compiled_tags)-1] = ""; + } } if (count($this->_tag_stack)>0) { @@ -309,23 +317,16 @@ /* Interleave the compiled contents and text blocks to get the final result. */ for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { - if ($compiled_tags[$i] == '') { - // tag result empty, remove first newline from following text block - $text_blocks[$i+1] = preg_replace('!^(\r\n|\r|\n)!', '', $text_blocks[$i+1]); + $lT = substr_count($template_tags_sp[$i], "\n"); + $lC = substr_count($compiled_tags[$i], "\n"); + if ($lC > $lT) { + $compiled_tags[$i] = trim(preg_replace('/[\r\n]+/s', ' ', $compiled_tags[$i])); + $lC = 0; } - $compiled_content .= $text_blocks[$i].$compiled_tags[$i]; + $compiled_tags[$i] .= str_repeat("<?php\n?".">", max($lT-$lC, 0)); + $compiled_content .= $text_blocks[$i].$compiled_tags[$i]; + $text_blocks[$i+1] = preg_replace('/^([\r\n]+)/s', '<?php echo "$1"; ?'.'>', $text_blocks[$i+1]); } $compiled_content .= $text_blocks[$i]; - /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */ - if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) { - $strip_tags = $_match[0]; - $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags); - $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified); - for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++) - $compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", - $this->_quote_replace($strip_tags_modified[$i]), - $compiled_content, 1); - } - // remove \n from the end of the file, if any if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) { @@ -338,5 +339,5 @@ // remove unnecessary close/open tags - $compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content); + $compiled_content = preg_replace('!\?'.'><\?php!', '', $compiled_content); // run compiled template through postfilter functions @@ -355,6 +356,6 @@ // put header at the top of the compiled template - $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; - $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n"; + $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S").""; + $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>"; /* Emit code to load needed plugins. */ @@ -369,5 +370,5 @@ } $_plugins_params .= '))'; - $plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n"; + $plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php'); smarty_core_load_plugins($_plugins_params, \$this); ?>"; $template_header .= $plugins_code; $this->_plugin_info = array(); @@ -376,5 +377,5 @@ if ($this->_init_smarty_vars) { - $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n"; + $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php'); smarty_core_assign_smarty_interface(null, \$this); ?>"; $this->_init_smarty_vars = false; } @@ -412,5 +413,5 @@ $_return = $this->_parse_var_props($tag_command . $tag_modifier, $this->_parse_attrs($tag_args)); if(isset($_tag_attrs['assign'])) { - return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>\n"; + return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>"; } else { return "<?php echo $_return; ?>" . $this->_additional_newline;
« previous php.smarty.dev (#2354) next »