You are viewing a plain text version of this content. The canonical link for it is here.
Posted to zeta-commits@incubator.apache.org by je...@apache.org on 2011/08/21 07:27:49 UTC

[zeta-commits] svn commit: r1159948 [1/4] - in /incubator/zetacomponents/trunk/Template: src/ src/functions/ src/structs/ src/unicode/ tests/ tests/regression_tests/functions/correct/

Author: jeromer
Date: Sun Aug 21 07:27:49 2011
New Revision: 1159948

URL: http://svn.apache.org/viewvc?rev=1159948&view=rev
Log:
- Fixed #ZETACOMP-15 : Template string functions are now multibyte safe

# Thanks Andreas Schamberger

Added:
    incubator/zetacomponents/trunk/Template/src/structs/lower_to_upper.php
    incubator/zetacomponents/trunk/Template/src/structs/upper_to_lower.php
    incubator/zetacomponents/trunk/Template/src/unicode/
    incubator/zetacomponents/trunk/Template/src/unicode/generate_unicode_tables.php
    incubator/zetacomponents/trunk/Template/tests/string_function_test.php
Modified:
    incubator/zetacomponents/trunk/Template/src/functions/string_code.php
    incubator/zetacomponents/trunk/Template/src/functions/string_functions.php
    incubator/zetacomponents/trunk/Template/src/template_autoload.php
    incubator/zetacomponents/trunk/Template/tests/regression_test.php
    incubator/zetacomponents/trunk/Template/tests/regression_tests/functions/correct/string_functions.in
    incubator/zetacomponents/trunk/Template/tests/regression_tests/functions/correct/string_functions.out
    incubator/zetacomponents/trunk/Template/tests/suite.php

Modified: incubator/zetacomponents/trunk/Template/src/functions/string_code.php
URL: http://svn.apache.org/viewvc/incubator/zetacomponents/trunk/Template/src/functions/string_code.php?rev=1159948&r1=1159947&r2=1159948&view=diff
==============================================================================
--- incubator/zetacomponents/trunk/Template/src/functions/string_code.php (original)
+++ incubator/zetacomponents/trunk/Template/src/functions/string_code.php Sun Aug 21 07:27:49 2011
@@ -27,7 +27,7 @@
 
 /**
  * This class contains a bundle of static functions, each implementing a specific
- * function used inside the template language. 
+ * function used inside the template language.
  *
  * @package Template
  * @version //autogen//
@@ -36,6 +36,100 @@
 class ezcTemplateString
 {
     /**
+     *  Stores the charset used for iconv
+     */
+    const CHARSET = 'utf-8';
+
+    /**
+     * Lower to upper case conversion table.
+     *
+     * Included on demand when mbstring functions are not available.
+     * Generated from http://www.unicode.org/Public/UNIDATA/UnicodeData.txt.
+     *
+     * @var array(string=>string)
+     */
+    private static $lowerToUpper = null;
+
+    /**
+     * Upper to lower case conversion table.
+     *
+     * Included on demand when mbstring functions are not available.
+     * Generated from http://www.unicode.org/Public/UNIDATA/UnicodeData.txt.
+     *
+     * @var array(string=>string)
+     */
+    private static $upperToLower = null;
+
+    /**
+     * Escapes all special chars within the given $charlist for using it within
+     * a regular expression character class definition (i.e []^-\ and the
+     * pattern delimiter). By default the pattern delimiter is '/'.
+     *
+     * @param string $charlist
+     * @param string $patternDelimiter
+     * @return string
+     */
+    private static function escapeCharacterClassCharlist( $charlist, $patternDelimiter = '/' )
+    {
+        return preg_replace( '!([\\\\\\-\\]\\[^'. $patternDelimiter .'])!', '\\\${1}', $charlist );
+    }
+
+    /**
+     * Multibyte safe ltrim().
+     *
+     * Based on http://de.php.net/manual/en/function.trim.php#72562.
+     * Added the charlist to the regexp. rtrim seems to be safe for whitespaces.
+     *
+     * @param string $str
+     * @param string $charlist
+     * @return string
+     */
+    public static function ltrim( $str, $charlist = null )
+    {
+        if ( is_null( $charlist ) )
+        {
+            return ltrim( $str );
+        }
+        $charlist = self::escapeCharacterClassCharlist( $charlist );
+        return preg_replace( '/^['.$charlist.']+/u', '', $str );
+    }
+
+    /**
+     * Multibyte safe rtrim().
+     *
+     * Based on http://de.php.net/manual/en/function.trim.php#72562.
+     * Added the charlist to the regexp. rtrim seems to be safe for whitespaces.
+     *
+     * @param string $str
+     * @param string $charlist
+     * @return string
+     */
+    public static function rtrim( $str, $charlist = null )
+    {
+        if ( is_null( $charlist ) )
+        {
+            return rtrim( $str );
+        }
+        $charlist = self::escapeCharacterClassCharlist( $charlist );
+        return preg_replace( '/['.$charlist.']+$/u', '', $str );
+    }
+
+    /**
+     * Multibyte safe str_pad().
+     *
+     * @param string $input
+     * @param int $pad_length
+     * @param string $pad_string
+     * @param mixed $pad_type
+     * @return string
+     */
+    public static function str_pad( $input, $pad_length, $pad_string = ' ', $pad_type = STR_PAD_RIGHT )
+    {
+        $diff = strlen( $input ) - iconv_strlen( $input, self::CHARSET );
+        return str_pad( $input, $pad_length + $diff, $pad_string, $pad_type );
+    }
+
+    /**
      * Returns the number of paragraphs found in the given string.
      *
      * @param string $string
@@ -43,10 +137,15 @@ class ezcTemplateString
      */
     public static function str_paragraph_count( $string )
     {
+        if ( iconv_strlen( $string, self::CHARSET ) == 0 )
+        {
+            return 0;
+        }
+
         $pos = 0;
-        $count = 0;
+        $count = 1;
 
-        while ( preg_match( "/\n\n+/", $string, $m, PREG_OFFSET_CAPTURE, $pos ) )
+        while ( preg_match( "/\n\n+/u", $string, $m, PREG_OFFSET_CAPTURE, $pos ) )
         {
             ++$count;
             $pos = $m[0][1] + 1;
@@ -54,7 +153,274 @@ class ezcTemplateString
 
         return $count;
     }
-}
 
+    /**
+     * Multibyte safe str_word_count().
+     *
+     * Based on http://de.php.net/manual/en/function.str-word-count.php#85592.
+     * Added the $charlist parameter and fixed the offsets for $format=2.
+     *
+     * @param string $string
+     * @param int $format
+     * 		0 - returns the number of words found
+     * 		1 - returns an array containing all the words found inside the string
+     * 		2 - returns an associative array, where the key is the numeric position of the word inside the string and the value is the actual word itself
+     * @param string $charlist
+     * @return mixed
+     */
+    public static function str_word_count( $string, $format = 0, $charlist = null )
+    {
+        if ( !is_null( $charlist ) )
+        {
+            $charlist = self::escapeCharacterClassCharlist( $charlist );
+        }
+        else
+        {
+            $charlist = '';
+        }
+
+        $pattern = "/\p{L}[\p{L}\p{Mn}\p{Pd}'\x{2019}".$charlist."]*/u";
+        $matches = array();
+        switch ( $format )
+        {
+            case 1:
+                preg_match_all( $pattern, $string, $matches );
+                return $matches[0];
+            case 2:
+                preg_match_all( $pattern, $string, $matches, PREG_OFFSET_CAPTURE );
+                $result = array();
+                $diff = 0;
+                foreach ( $matches[0] as $match )
+                {
+                    // reduce wrong offset by multibyte difference
+                    $offset = $match[1] - $diff;
+                    $result[$offset] = $match[0];
+                    // add multibyte offset difference of current word
+                    $diff += strlen( $match[0] ) - iconv_strlen( $match[0], self::CHARSET );
+                }
+                return $result;
+            default:
+                return preg_match_all( $pattern, $string, $matches );
+         }
+    }
+
+    /**
+     * Multibyte safe strpos().
+     *
+     * @param string $haystack
+     * @param mixed $needle
+     * @param int $offset
+     * @return int|bool
+     */
+    public static function strpos( $haystack, $needle, $offset = 0 )
+    {
+        return iconv_strpos( $haystack, $needle, $offset, self::CHARSET );
+    }
+
+    /**
+     * Multibyte safe strrpos().
+     *
+     * @param string $haystack
+     * @param mixed $needle
+     * @param int $offset
+     * @param bool $useMbString
+     * @return string
+     */
+    public static function strrpos( $haystack, $needle, $offset = 0, $useMbString = true )
+    {
+        if ( $useMbString === true && function_exists( 'mb_strrpos' ) )
+        {
+            return mb_strrpos( $haystack, $needle, $offset, self::CHARSET );
+        }
+        else
+        {
+            $addOffset = 0;
+            if ( $offset > 0 )
+            {
+                $haystack = iconv_substr( $haystack, $offset, iconv_strlen( $haystack, self::CHARSET ), self::CHARSET );
+                $addOffset = $offset;
+            }
+            elseif( $offset < 0 )
+            {
+                $haystack = iconv_substr( $haystack, 0, $offset, self::CHARSET );
+            }
+            $result = iconv_strrpos( $haystack, $needle, self::CHARSET );
+            return ( $result === false ) ? $result : $result + $addOffset;
+        }
+    }
+
+    /**
+     * Multibyte safe strrev().
+     *
+     * Based on http://de.php.net/manual/en/function.strrev.php#62422.
+     *
+     * @param string $str
+     * @return string
+     */
+    public static function strrev( $string )
+    {
+        if ( empty( $string ) )
+        {
+            return $string;
+        }
+        $matches = array();
+        preg_match_all( '/./us', $string, $matches );
+        return implode( '', array_reverse( $matches[0] ) );
+    }
+
+    /**
+     * Multibyte safe strtolower().
+     * Uses mb_strtolower() if available otherwise falls back to own conversion
+     * table.
+     *
+     * @param string $str
+     * @param bool $useMbString
+     * @return string
+     */
+    public static function strtolower( $str, $useMbString = true )
+    {
+        if ( empty( $str ) )
+        {
+            return $str;
+        }
+        if ( $useMbString === true && function_exists( 'mb_strtolower' ) )
+        {
+            return mb_strtolower( $str, self::CHARSET );
+        }
+        if ( is_null( self::$upperToLower ) )
+        {
+            self::$upperToLower = new ezcTemplateStringUpperToLowerUnicodeMap();
+        }
+        return strtr( $str, self::$upperToLower->unicodeTable );
+    }
+
+    /**
+     * Multibyte safe strtoupper().
+     * Uses mb_strtoupper() if available otherwise falls back to own conversion
+     * table.
+     *
+     * @param string $str
+     * @param bool $useMbString
+     * @return string
+     */
+    public static function strtoupper( $str, $useMbString = true )
+    {
+        if ( empty( $str ) )
+        {
+            return $str;
+        }
+        if ( $useMbString === true && function_exists( 'mb_strtoupper' ) )
+        {
+            return mb_strtoupper( $str, self::CHARSET );
+        }
+        if ( is_null( self::$lowerToUpper ) )
+        {
+            self::$lowerToUpper = new ezcTemplateStringLowerToUpperUnicodeMap();
+        }
+        return strtr( $str, self::$lowerToUpper->unicodeTable );
+    }
+
+    /**
+     * Multibyte safe trim().
+     *
+     * @param string $str
+     * @param string $charlist
+     * @return string
+     */
+    public static function trim( $str, $charlist = null )
+    {
+        if ( is_null( $charlist ) )
+        {
+            return trim( $str );
+        }
+        return self::ltrim( self::rtrim( $str, $charlist ), $charlist );
+    }
+
+    /**
+     * Multibyte safe ucfirst().
+     *
+     * @param string $string
+     * @return string
+     */
+    public static function ucfirst( $string )
+    {
+        $strlen = iconv_strlen( $string, self::CHARSET );
+        if ( $strlen == 0 )
+        {
+            return '';
+        }
+        elseif ( $strlen == 1 )
+        {
+            return self::strtoupper( $string );
+        }
+        else
+        {
+            $matches = array();
+            preg_match( '/^(.{1})(.*)$/us', $string, $matches );
+            return self::strtoupper( $matches[1] ) . $matches[2];
+        }
+    }
+
+    /**
+     * Multibyte safe wordwrap().
+     *
+     * @param string $str
+     * @param int $width
+     * @param string $break
+     * @param bool $cut
+     * @return string
+     */
+    public static function wordwrap( $str, $width = 75, $break = "\n", $cut = false )
+    {
+        if ( empty( $str ) )
+        {
+            return $str;
+        }
+        $lines = explode( $break, $str );
+        $return = '';
+        foreach ( $lines as $line )
+        {
+            $line = trim( $line );
+            $length = iconv_strlen( $line, self::CHARSET );
+            if ( $length > $width )
+            {
+                if ( $cut )
+                {
+                    while ( $length > $width )
+                    {
+                        $return .= iconv_substr( $line, 0, $width, self::CHARSET ) . $break;
+                        $line = iconv_substr( $line, $width, iconv_strlen( $line, self::CHARSET ) - $width, self::CHARSET );
+                        $length = iconv_strlen( $line, self::CHARSET );
+                    }
+                    $return .= $line;
+                }
+                else
+                {
+                    $words = explode( ' ', $line );
+                    $tmp = '';
+                    foreach ( $words as $word )
+                    {
+                        $word = trim( $word );
+                        if ( iconv_strlen( $tmp . $word, self::CHARSET ) <= $width )
+                        {
+                            $tmp .= $word . ' ';
+                        }
+                        else
+                        {
+                            $return .= trim( $tmp ) . $break;
+                            $tmp = $word . ' ';
+                        }
+                    }
+                    $return .= trim( $tmp );
+                }
+            }
+            else
+            {
+                $return .= $line. $break;
+            }
+        }
+        return $return;
+    }
+}
 
-?>
+?>
\ No newline at end of file

Modified: incubator/zetacomponents/trunk/Template/src/functions/string_functions.php
URL: http://svn.apache.org/viewvc/incubator/zetacomponents/trunk/Template/src/functions/string_functions.php?rev=1159948&r1=1159947&r2=1159948&view=diff
==============================================================================
--- incubator/zetacomponents/trunk/Template/src/functions/string_functions.php (original)
+++ incubator/zetacomponents/trunk/Template/src/functions/string_functions.php Sun Aug 21 07:27:49 2011
@@ -33,7 +33,12 @@
 class ezcTemplateStringFunctions extends ezcTemplateFunctions
 {
     /**
-     * Translates a function used in the Template language to a PHP function call.  
+     *  Stores the charset used for iconv
+     */
+    const CHARSET = 'utf-8';
+
+    /**
+     * Translates a function used in the Template language to a PHP function call.
      * The function call is represented by an array with three elements:
      *
      * 1. The return typehint. Is it an array, a non-array, or both.
@@ -50,46 +55,53 @@ class ezcTemplateStringFunctions extends
         {
             // str_replace( $sl, $index, $len, $sr )
             // substr( $sl, 0, $index ) . $sr . substr( $sl, $index + $len );
-            case "str_replace": 
-                return array( ezcTemplateAstNode::TYPE_VALUE, array( "%left", "%index", "%length", "%right" ), 
-                   self::concat( 
-                       self::functionCall( "substr", array( "%left", self::value( 0 ), "%index" ) ),
-                       self::concat( 
-                           "%right", 
-                           self::functionCall( 
-                               "substr", 
-                               array( "%left", array( "ezcTemplateAdditionOperatorAstNode", array( "%index", "%length" ) ) ) 
-                           ) 
-                       ) 
+            case "str_replace":
+                return array( ezcTemplateAstNode::TYPE_VALUE, array( "%left", "%index", "%length", "%right" ),
+                   self::concat(
+                       self::functionCall( "iconv_substr", array( "%left", self::value( 0 ), "%index", self::value( self::CHARSET ) ) ),
+                       self::concat(
+                           "%right",
+                           self::functionCall(
+                               "iconv_substr",
+                               array( "%left", array( "ezcTemplateAdditionOperatorAstNode", array( "%index", "%length" ) ),
+                                    self::functionCall( "iconv_strlen", array("%left", self::value( self::CHARSET ) ) ), self::value( self::CHARSET ) )
+                           )
+                       )
                     )
-                 ); 
+                 );
 
 
-            // str_remove( $s, $index, $len ) 
+            // str_remove( $s, $index, $len )
             // substr( $s, 0, $index ) . substr( $s, $index + $len );
-             case "str_remove": 
-                 return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%index", "%length" ), 
-                   self::concat( 
-                       self::functionCall( "substr", array( "%string", self::value( 0 ), "%index" ) ),
-                       self::functionCall( "substr", array( "%string", array( "ezcTemplateAdditionOperatorAstNode", array( "%index", "%length" ) ) ) 
-                           ) 
-                        ) 
+             case "str_remove":
+                 return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%index", "%length" ),
+                   self::concat(
+                       self::functionCall( "iconv_substr", array( "%string", self::value( 0 ), "%index", self::value( self::CHARSET ) ) ),
+                       self::functionCall( "iconv_substr",
+                       array( "%string", array( "ezcTemplateAdditionOperatorAstNode", array( "%index", "%length" ) ),
+                              self::functionCall( "iconv_strlen", array("%string", self::value( self::CHARSET ) ) ), self::value( self::CHARSET ) )
+                           )
+                        )
                      );
-  
+
             // string str_chop( $s, $len ) ( QString::chop ):
             // substr( $s, 0, strlen( $string ) - $len );
-             case "str_chop": 
-                return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%length" ), 
-                       self::functionCall( "substr", array( 
-                           "%string", 
-                           self::value( 0 ), 
-                           array( "ezcTemplateSubtractionOperatorAstNode", array( self::functionCall( "strlen", array( "%string" ) ), "%length" ) )
+             case "str_chop":
+                return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%length" ),
+                       self::functionCall( "iconv_substr", array(
+                           "%string",
+                           self::value( 0 ),
+                           array( "ezcTemplateSubtractionOperatorAstNode", array( self::functionCall( "iconv_strlen", array( "%string", self::value( self::CHARSET ) ) ), "%length" ) ),
+                           self::value( self::CHARSET )
                        )
                    ) );
-                       
+
             // string str_chop_front( $s, $len )
             // substr( $s, $len );
-             case "str_chop_front": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length" ), self::functionCall( "substr", array( "%string", "%length" ) ) );
+            case "str_chop_front":
+                return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length" ),
+                    self::functionCall( "iconv_substr", array( "%string", "%length",
+                        self::functionCall( "iconv_strlen", array("%string", self::value( self::CHARSET ) ) ), self::value( self::CHARSET ) ) ) );
 
              case "str_append": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%left", "%right" ), self::concat( "%left", "%right" ) );
 
@@ -104,193 +116,196 @@ class ezcTemplateStringFunctions extends
             case "str_nat_compare": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%left", "%right"), self::functionCall( "strnatcmp", array( "%left", "%right" ) ) );
 
             // str_contains( $sl, $sr ) ( QString::compare )::
-            // strpos( $sl, $sr ) !== false 
-            case "str_contains": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%left", "%right" ), 
-                array( "ezcTemplateNotIdenticalOperatorAstNode", 
-                array( self::functionCall( "strpos", array( "%left", "%right" ) ), self::value( false ) ) ) );
+            // strpos( $sl, $sr ) !== false
+            case "str_contains": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%left", "%right" ),
+                array( "ezcTemplateNotIdenticalOperatorAstNode",
+                array( self::functionCall( "ezcTemplateString::strpos", array( "%left", "%right" ) ), self::value( false ) ) ) );
 
             // str_len( $s )
             // strlen( $s )
-            case "str_len": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), self::functionCall( "strlen", array( "%string" ) ) );
+            case "str_len": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), self::functionCall( "iconv_strlen", array( "%string", self::value( self::CHARSET ) ) ) );
 
             // str_left( $s, $len )
             // substr( $s, 0, $len )
-            case "str_left": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length"), self::functionCall( "substr", array( "%string", self::value( 0 ), "%length" ) ) );
+            case "str_left": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length"), self::functionCall( "iconv_substr", array( "%string", self::value( 0 ), "%length", self::value( self::CHARSET ) ) ) );
 
             // str_starts_with( $sl, $sr )
             // strpos( $sl, $sr ) === 0
             case "str_starts_with": return array(
-                ezcTemplateAstNode::TYPE_VALUE,  
-                array( "%haystack", "%needle" ), 
-                array( "ezcTemplateIdenticalOperatorAstNode", array( 
-                    self::functionCall( "strpos", array( "%haystack", "%needle" ) ),
+                ezcTemplateAstNode::TYPE_VALUE,
+                array( "%haystack", "%needle" ),
+                array( "ezcTemplateIdenticalOperatorAstNode", array(
+                    self::functionCall( "ezcTemplateString::strpos", array( "%haystack", "%needle" ) ),
                     self::value( 0 )
-                ) ) ); 
+                ) ) );
 
             // str_right( $s, $len )
             // substr( $s, -$len )
-            case "str_right": return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%length" ),
-                self::functionCall( "substr", array( "%string", array( "ezcTemplateArithmeticNegationOperatorAstNode",  array( "%length" ) ) ) ) );
+            case "str_right":
+                return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%length" ),
+                    self::functionCall(
+                        "iconv_substr", array( "%string", array( "ezcTemplateArithmeticNegationOperatorAstNode", array( "%length" ) ),
+                        self::functionCall( "iconv_strlen", array("%string", self::value( self::CHARSET ) ) ), self::value( self::CHARSET ) ) ) );
 
             // str_ends_with( $sl, $sr )
             // strrpos( $sl, $sr ) === ( strlen( $sl ) - strlen( $sr) )
             case "str_ends_with": return array(
                 ezcTemplateAstNode::TYPE_VALUE,
                 array( "%haystack", "%needle" ),
-                array( "ezcTemplateIdenticalOperatorAstNode", array( 
-                    self::functionCall( "strrpos", array( "%haystack", "%needle" ) ),
-                    array( "ezcTemplateSubtractionOperatorAstNode", array( 
-                        self::functionCall( "strlen", array( "%haystack" ) ), 
-                        self::functionCall( "strlen", array( "%needle" ) ) 
-                    ) ) ) ) ); 
+                array( "ezcTemplateIdenticalOperatorAstNode", array(
+                    self::functionCall( "ezcTemplateString::strrpos", array( "%haystack", "%needle" ) ),
+                    array( "ezcTemplateSubtractionOperatorAstNode", array(
+                        self::functionCall( "iconv_strlen", array( "%haystack", self::value( self::CHARSET ) ) ),
+                        self::functionCall( "iconv_strlen", array( "%needle", self::value( self::CHARSET ) ) )
+                    ) ) ) ) );
 
             // str_mid( $s, $index, $len )
             // substr( $s, $index, $len )
-            case "str_mid": return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%index", "%length" ), 
-                self::functionCall( "substr", array( "%string", "%index", "%length" ) ) );
+            case "str_mid": return array( ezcTemplateAstNode::TYPE_VALUE,  array( "%string", "%index", "%length" ),
+                self::functionCall( "iconv_substr", array( "%string", "%index", "%length", self::value( self::CHARSET ) ) ) );
 
             // str_at( $s, $index )
             // substr( $s, $index, 1 )
-            case "str_at": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%index" ), 
-                self::functionCall( "substr", array( "%string", "%index", self::value( 1 ) ) ) );
+            case "str_at": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%index" ),
+                self::functionCall( "iconv_substr", array( "%string", "%index", self::value( 1 ), self::value( self::CHARSET ) ) ) );
 
             // str_fill( $s, $len )
             // str_repeat( $s, $len )
-            case "str_fill": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length" ), 
+            case "str_fill": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length" ),
                 self::functionCall( "str_repeat", array( "%string", "%length" ) ) );
 
             // str_index_of( $sl, $sr [, $index ] )
             // strpos( $sl, $sr [, $index ] )
-            case "str_index_of": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%haystack", "%needle", "[%index]" ), 
-                self::functionCall( "strpos", array( "%haystack", "%needle", "[%index]" ) ) );
-            
+            case "str_index_of": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%haystack", "%needle", "[%index]" ),
+                self::functionCall( "ezcTemplateString::strpos", array( "%haystack", "%needle", "[%index]" ) ) );
+
             // str_last_index( $sl, $sr [, $index] )
             // strrpos( $sl, $sr [, $index ] )
-            case "str_last_index": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%haystack", "%needle", "[%index]" ), 
-                self::functionCall( "strrpos", array( "%haystack", "%needle", "[%index]" ) ) );
-             
+            case "str_last_index": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%haystack", "%needle", "[%index]" ),
+                self::functionCall( "ezcTemplateString::strrpos", array( "%haystack", "%needle", "[%index]" ) ) );
+
             // str_is_empty( $s )
             // strlen( $s ) === 0
-            case "str_is_empty": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                array( "ezcTemplateIdenticalOperatorAstNode", array( 
-                    self::functionCall( "strlen", array( "%string" ) ),
+            case "str_is_empty": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                array( "ezcTemplateIdenticalOperatorAstNode", array(
+                    self::functionCall( "iconv_strlen", array( "%string", self::value( self::CHARSET ) ) ),
                     self::value( 0 ) ) ) );
-             
+
             // str_pad_left( $s, $len, $fill )
             // str_pad( $s, $len, $fill, STR_PAD_LEFT )
-            case "str_pad_left": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length", "%fill" ), 
-                    self::functionCall( "str_pad", array( "%string", "%length", "%fill", self::constant( "STR_PAD_LEFT" ) ) ) );
-             
+            case "str_pad_left": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length", "%fill" ),
+                    self::functionCall( "ezcTemplateString::str_pad", array( "%string", "%length", "%fill", self::constant( "STR_PAD_LEFT" ) ) ) );
+
             // str_pad_right( $s, $len, $fill ) ( QString::rightJustified() )::
             // str_pad( $s, $len, $fill, STR_PAD_RIGHT )
-            case "str_pad_right": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length", "%fill" ), 
-                    self::functionCall( "str_pad", array( "%string", "%length", "%fill", self::constant( "STR_PAD_RIGHT" ) ) ) );
-             
+            case "str_pad_right": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%length", "%fill" ),
+                    self::functionCall( "ezcTemplateString::str_pad", array( "%string", "%length", "%fill", self::constant( "STR_PAD_RIGHT" ) ) ) );
+
             // str_number( $num, $decimals, $point, $sep )
             // number_format( $num, $decimals, $point, $sep )
-            case "str_number": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%number", "%decimals", "%point", "%separator" ), 
+            case "str_number": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%number", "%decimals", "%point", "%separator" ),
                     self::functionCall( "number_format", array( "%number", "%decimals", "%point", "%separator") ) );
-             
+
             // str_trim( $s [, $chars ] )
             // trim( $s [, $chars] )
-            case "str_trim": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%chars]" ), 
-                    self::functionCall( "trim", array( "%string", "[%chars]") ) );
-             
+            case "str_trim": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%chars]" ),
+                    self::functionCall( "ezcTemplateString::trim", array( "%string", "[%chars]") ) );
+
             // str_trim_left( $s [, $chars] )
             // ltrim( $s [, $chars] )
-            case "str_trim_left": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%chars]" ), 
-                    self::functionCall( "ltrim", array( "%string", "[%chars]") ) );
-             
+            case "str_trim_left": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%chars]" ),
+                    self::functionCall( "ezcTemplateString::ltrim", array( "%string", "[%chars]") ) );
+
             // str_trim_right( $s [, $chars] )
             // rtrim( $s, [$chars] )
-            case "str_trim_right": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%chars]" ), 
-                    self::functionCall( "rtrim", array( "%string", "[%chars]") ) );
-             
+            case "str_trim_right": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%chars]" ),
+                    self::functionCall( "ezcTemplateString::rtrim", array( "%string", "[%chars]") ) );
+
             // str_simplified( $s )
             // trim( preg_replace( "/(\n|\t|\r\n|\s)+/", " ", $s ) )
-            case "str_simplify": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                    self::functionCall( "trim", array(
+            case "str_simplify": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                    self::functionCall( "ezcTemplateString::trim", array(
                         self::functionCall( "preg_replace", array( self::constant( '"/(\n|\t|\r\n|\s)+/"' ), self::value( " " ), "%string" ) )
                     ) ) );
-             
+
             // str_split( $s, $sep[, $max] )
             // explode( $s, $sep, $max )
-            case "str_split": return array( ezcTemplateAstNode::TYPE_VALUE | ezcTemplateAstNode::TYPE_ARRAY, array( "%string", "%separator", "[%max]" ), 
+            case "str_split": return array( ezcTemplateAstNode::TYPE_VALUE | ezcTemplateAstNode::TYPE_ARRAY, array( "%string", "%separator", "[%max]" ),
                     self::functionCall( "explode", array( "%separator", "%string", "[%max]" ) ) );
-             
+
             // str_join( $s_list, $sep )
             // join( $sList, $sep )
-            case "str_join": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%list", "%separator" ), 
+            case "str_join": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%list", "%separator" ),
                     self::functionCall( "join", array( "%separator", "%list" ) ) );
-             
+
             // str_printf( $format [...] )
             // sprintf( $format [...] )
             // TODO
-             
+
             // str_chr( $ord1 [, $ord2...] )::
             // ord( $ord1 ) [ . ord( $ord2 ) ...]
-            // TODO 
-            
+            // TODO
+
             // str_ord( $c )
             // ord( $c )
-            case "str_ord": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%char" ), 
+            case "str_ord": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%char" ),
                     self::functionCall( "ord", array( "%char" ) ) );
 
             // chr( $c )
-            case "str_chr": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%char" ), 
+            case "str_chr": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%char" ),
                     self::functionCall( "chr", array( "%char" ) ) );
-            
+
             // str_ord_list( $s )::
             // chr( $s[0] ) [ . chr( $s[1] ) ]
             // TODO
-             
+
             // str_upper( $s )
             // strtoupper( $s )
-            case "str_upper": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                    self::functionCall( "strtoupper", array( "%string") ) );
-            
+            case "str_upper": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                    self::functionCall( "ezcTemplateString::strtoupper", array( "%string") ) );
+
             // str_lower( $s )
             // strtolower( $s )
-            case "str_lower": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                    self::functionCall( "strtolower", array( "%string") ) );
-             
+            case "str_lower": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                    self::functionCall( "ezcTemplateString::strtolower", array( "%string") ) );
+
             // str_capitalize( $s )::
             // ucfirst( $s )
-            case "str_capitalize": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                    self::functionCall( "ucfirst", array( "%string") ) );
-             
+            case "str_capitalize": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                    self::functionCall( "ezcTemplateString::ucfirst", array( "%string") ) );
+
             // str_find_replace( $s, $find, $replace, $count )::
             // str_replace( $s, $replace, $find, $count )
-            case "str_find_replace": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%find", "%replace", "[%count]" ), 
+            case "str_find_replace": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%find", "%replace", "[%count]" ),
                     self::functionCall( "str_replace", array( "%find", "%replace", "%string", "[%count]") ) );
-             
+
             // str_reverse( $s )::
             // strrev( $s )
-            case "str_reverse": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                    self::functionCall( "strrev", array( "%string" ) ) );
-             
+            case "str_reverse": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                    self::functionCall( "ezcTemplateString::strrev", array( "%string" ) ) );
+
             // str_section( $s, $sep, $start, $end = -1 )
             // join( array_slice( split( $s, $sep, $end != -1 ? $end, false ), $start, $end ? $end : false ) )
             // TODO
 
              // str_char_count( $s )::
             // strlen( $s )
-            case "str_char_count": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
-                    self::functionCall( "strlen", array( "%string" ) ) );
-             
+            case "str_char_count": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
+                    self::functionCall( "iconv_strlen", array( "%string", self::value( self::CHARSET ) ) ) );
+
             // str_word_count( $s [, $wordsep] )
             // str_word_count( $s, 0 [, $wordsep] )
-            case "str_word_count": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%wordsep]" ), 
-                    self::functionCall( "str_word_count", array( "%string", self::value( 0 ), "[%wordsep]" ) ) );
- 
+            case "str_word_count": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "[%wordsep]" ),
+                    self::functionCall( "ezcTemplateString::str_word_count", array( "%string", self::value( 0 ), "[%wordsep]" ) ) );
+
             // - *string* str_paragraph_count( $s )::
             // Code.
-            case "str_paragraph_count": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), 
+            case "str_paragraph_count": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ),
                     self::functionCall( "ezcTemplateString::str_paragraph_count", array( "%string" ) ) );
- 
-           // 
+
+           //
             // - *string* str_sentence_count( $s )::
-            // 
+            //
             //     $pos = 0;
             //     $count = 0;
             //     while ( preg_match( "/. /", $s, $m, PREG_OFFSET_CAPTURE, $pos )
@@ -299,40 +314,40 @@ class ezcTemplateStringFunctions extends
             //         $pos = $m[0][1];
             //     }
             // TODO
-            // 
+            //
             // - *string* str_break( $s, $eol = contextaware, $lbreak = contextaware )::
-            // 
+            //
             //     str_replace( context_eol_char( $eol ), context_linebreak_char( $eol ), $s )
-            // 
+            //
             // TODO
-            // 
+            //
             // - *string* str_break_chars( $s, $cbreak )::
-            // 
+            //
             //     $sNew = '';
             //     for ( $i = 0; $i < strlen( $s ) - 1; ++$i )
             //     {
             //         $sNew .= $s[$i] . $cbreak;
             //     }
             //     $sNew .= $s[strlen( $s ) - 1];
-            // 
+            //
             // TODO
-            
+
             // str_wrap( $s, $width, $break, $cut )
             // wordwrap( $s, $width, $break, $cut )
-            case "str_wrap": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%width", "%break", "[%cut]" ), 
-                    self::functionCall( "wordwrap", array( "%string", "%width", "%break", "[%cut]" ) ) );
+            case "str_wrap": return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string", "%width", "%break", "[%cut]" ),
+                    self::functionCall( "ezcTemplateString::wordwrap", array( "%string", "%width", "%break", "[%cut]" ) ) );
 
             // base64_encode( $s )
             case "str_base64_encode":
                 return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), self::functionCall( "base64_encode", array( "%string" ) ) );
-            
+
             // base64_decode( $s )
             case "str_base64_decode":
                 return array( ezcTemplateAstNode::TYPE_VALUE, array( "%string" ), self::functionCall( "base64_decode", array( "%string" ) ) );
- 
-            // 
+
+            //
             // - *string* str_wrap_indent::
-            // 
+            //
             //    $tmp = wordwrap( $s, $width, $break, $cut )
             //    $lines = explode( "\n", $tmp );
             //    $newLines = array();
@@ -341,71 +356,71 @@ class ezcTemplateStringFunctions extends
             //        $newLines[] = $prefix . $line . $suffix;
             //    }
             //    return join( "\n", $newLines )
-            // 
+            //
             // TODO
             // - *string* str_block( $s, $prefix, $suffix )
-            // 
-            // 
+            //
+            //
             // - *string* str_shorten_right( $s, $max_size )
-            // 
+            //
             // - *string* str_shorten_mid( $s, $max_size )
-            // 
+            //
             // - *string* str_shorten_left( $s, $max_size )
-            // 
+            //
             // - *string* str_crc32( $s )::
-            // 
+            //
             //     crc32( $s )
-            // 
+            //
             // - *string* str_md5( $s )::
-            // 
+            //
             //     md5( $s )
-            // 
+            //
             // - *string* str_sha1( $s )::
-            // 
+            //
             //     sha1( $s )
-            // 
+            //
             // - *string* str_rot13( $s )::
-            // 
+            //
             //     str_rot13( $s )
-            // 
+            //
             // Some of the functions are also available as case insensitive versions, they are:
-            // 
+            //
             // - *string* stri_contains( $sl, $sr ) ( QString::compare )::
-            // 
+            //
             //     stristr( $sl, $sr ) !== false
-            // 
+            //
             // - *string* stri_starts_with( $sl, $sr ) ( QString::startsWith )::
-            // 
+            //
             //     stripos( strtolower( $sl ), strtolower( $sr ) ) === 0
-            // 
+            //
             // - *string* stri_ends_with( $sl, $sr ) ( QString::endsWith )::
-            // 
+            //
             //     strripos( $sl, $sr ) === ( strlen( $sl ) - 1 )
-            // 
+            //
             // - *string* stri_index( $sl, $sr [, $from] ) ( QString::indexOf )::
-            // 
+            //
             //     stripos( $sl, $sr [, $from ] )
-            // 
+            //
             // - *string* stri_last_index( $sl, $sr [, $from] ) ( QString::lastIndexOf )::
-            // 
+            //
             //     strirpos( $sl, $sr [, $from ] )
-            // 
+            //
             // - *string* stri_find_replace( $s, $find, $replace, $count )::
-            // 
+            //
             //     str_ireplace( $s, $replace, $find, $count )
-            // 
+            //
             // - *string* stri_compare( $sl, $sr ) ( QString::compare )::
-            // 
+            //
             //     strcasecmp( $sl, $sr );
-            // 
+            //
             // - *string* stri_nat_compare( $sl, $sr )::
-            // 
+            //
             //     strnatcasecmp( $sl, $sr );
-            // 
+            //
 
         }
 
         return null;
     }
 }
-?>
+?>
\ No newline at end of file