Index: StringFunctions.body.php =================================================================== --- StringFunctions.body.php (revision 0) +++ StringFunctions.body.php (revision 0) @@ -0,0 +1,297 @@ +mUniqPrefix, '/' ); + + if ( defined('Parser::MARKER_SUFFIX') ) + $suffix = preg_quote( Parser::MARKER_SUFFIX, '/' ); + elseif ( isset($parser->mMarkerSuffix) ) + $suffix = preg_quote( $parser->mMarkerSuffix, '/' ); + elseif ( defined('MW_PARSER_VERSION') && strcmp( MW_PARSER_VERSION, '1.6.1' ) > 0 ) + $suffix = "QINU\x07"; + else $suffix = 'QINU'; + + self::$markerRegex = '/' .$prefix. '(?:(?!' .$suffix. ').)*' . $suffix . '/us'; + + wfProfileOut( __METHOD__ ); + return true; + } + + // Removes unique markers from passed parameters. + private function killMarkers ( $text ) { + if( self::$markerRegex ) { + return preg_replace( self::$markerRegex , '' , $text ); + } else { + return $text; + } + } + + // Verifies parameter is less than max string length. + private function checkLength( $text ) { + global $wgStringFunctionsLimit; + return ( mb_strlen( $text ) < $wgStringFunctionsLimit ); + } + + // Generates error message. Called when string is too long. + private function tooLongError() { + global $wgStringFunctionsLimit; + return wfMsg( 'stringfunctions-toolong', $wgStringFunctionsLimit ); + } + + /** + * {{#len:string}} + */ + function runLen ( &$parser, $inStr = '' ) { + wfProfileIn( __METHOD__ ); + + $inStr = $this->killMarkers( (string)$inStr ); + $len = mb_strlen( $inStr ); + + wfProfileOut( __METHOD__ ); + return $len; + } + + /** + * {{#pos:value|key|offset}} + * Note: If the needle is an empty string, single space is used instead. + * Note: If the needle is not found, -1 is returned. + */ + function runPos ( &$parser, $inStr = '', $inNeedle = '', $inOffset = 0 ) { + wfProfileIn( __METHOD__ ); + + $inStr = $this->killMarkers( (string)$inStr ); + $inNeedle = $this->killMarkers( (string)$inNeedle ); + + if( !$this->checkLength( $inStr ) || + !$this->checkLength( $inNeedle ) ) { + wfProfileOut( __METHOD__ ); + return $this->tooLongError(); + } + + if( $inNeedle == '' ) { $inNeedle = ' '; } + + $pos = mb_strpos( $inStr, $inNeedle, $inOffset ); + if( $pos === false ) { $pos = -1; } + + wfProfileOut( __METHOD__ ); + return $pos; + } + + /** + * {{#rpos:value|key}} + * Note: If the needle is an empty string, single space is used instead. + * Note: If the needle is not found, -1 is returned. + */ + function runRPos ( &$parser, $inStr = '', $inNeedle = '' ) { + wfProfileIn( __METHOD__ ); + + $inStr = $this->killMarkers( (string)$inStr ); + $inNeedle = $this->killMarkers( (string)$inNeedle ); + + if( !$this->checkLength( $inStr ) || + !$this->checkLength( $inNeedle ) ) { + wfProfileOut( __METHOD__ ); + return $this->tooLongError(); + } + + if( $inNeedle == '' ) { $inNeedle = ' '; } + + $pos = mb_strrpos( $inStr, $inNeedle ); + if( $pos === false ) { $pos = -1; } + + wfProfileOut( __METHOD__ ); + return $pos; + } + + /** + * {{#sub:value|start|length}} + * Note: If length is zero, the rest of the input is returned. + */ + function runSub ( &$parser, $inStr = '', $inStart = 0, $inLength = 0 ) { + wfProfileIn( __METHOD__ ); + + $inStr = $this->killMarkers( (string)$inStr ); + + if( !$this->checkLength( $inStr ) ) { + wfProfileOut( __METHOD__ ); + return $this->tooLongError(); + } + + if ( intval($inLength) == 0 ) { + $result = mb_substr( $inStr, $inStart ); + } else { + $result = mb_substr( $inStr, $inStart, $inLength ); + } + + wfProfileOut( __METHOD__ ); + return $result; + } + + /** + * {{#replace:value|from|to}} + * Note: If "from" is an empty string, single space is used instead. + */ + function runReplace( &$parser, $inStr = '', $inReplaceFrom = '', $inReplaceTo = '' ) { + global $wgStringFunctionsLimit; + wfProfileIn( __METHOD__ ); + + $inStr = $this->killMarkers( (string)$inStr ); + $inReplaceFrom = $this->killMarkers( (string)$inReplaceFrom ); + $inReplaceTo = $this->killMarkers( (string)$inReplaceTo ); + + if( !$this->checkLength( $inStr ) || + !$this->checkLength( $inReplaceFrom ) || + !$this->checkLength( $inReplaceTo ) ) { + wfProfileOut( __METHOD__ ); + return $this->tooLongError(); + } + + if( $inReplaceFrom == '' ) { $inReplaceFrom = ' '; } + + // Precompute limit to avoid generating enormous string: + $diff = mb_strlen( $inReplaceTo ) - mb_strlen( $inReplaceFrom ); + if( $diff > 0 ) { + $limit = ( ( $wgStringFunctionsLimit - mb_strlen( $inStr ) ) / $diff ) + 1; + } else { + $limit = -1; + } + + $inReplaceFrom = preg_quote( $inReplaceFrom, '/' ); + $inReplaceTo = preg_quote( $inReplaceTo, '/' ); + + $result = preg_replace( '/' . $inReplaceFrom . '/u', $inReplaceTo, $inStr, $limit); + + if( !$this->checkLength( $result ) ) { + wfProfileOut( __METHOD__ ); + return $this->tooLongError(); + } + + wfProfileOut( __METHOD__ ); + return $result; + } + + + /** + * {{#explode:value|delimiter|position}} + * Note: Negative position can be used to specify tokens from the end. + * Note: If the divider is an empty string, single space is used instead. + * Note: Empty string is returned, if there is not enough exploded chunks. + */ + function runExplode ( &$parser, $inStr = '', $inDiv = '', $inPos = 0 ) { + wfProfileIn( __METHOD__ ); + + $inStr = $this->killMarkers( (string)$inStr ); + $inDiv = $this->killMarkers( (string)$inDiv ); + + if( $inDiv == '' ) { $inDiv = ' '; } + + $inStr = preg_quote( $inStr, '/' ); + $inDiv = preg_quote( $inDiv, '/' ); + + if( !$this->checkLength( $inStr ) || + !$this->checkLength( $inDiv ) ) { + wfProfileOut( __METHOD__ ); + return $this->tooLongError(); + } + + $matches = preg_split( '/'.$inDiv.'/u', $inStr ); + + if( $inPos >= 0 && isset( $matches[$inPos] ) ) { + $result = $matches[$inPos]; + } elseif ( $inPos < 0 && isset( $matches[count($matches) + $inPos] ) ) { + $result = $matches[count($matches) + $inPos]; + } else { + $result = ''; + } + + wfProfileOut( __METHOD__ ); + return $result; + } +} + + +/* + The following section reimplements the necessary multi-byte + string functions if they are not present. This removes multi-byte + support as a requirement for this extension; however, these + substitutes are very inefficient. True multi-byte support is + recommended. + + In addition, please note that the only parameters implemented below + are the ones necessary for this extension. +*/ + +if( !function_exists( 'mb_strlen' ) ) { + + function mb_strlen( $text ) { + if( $text == '' ) { return 0; } + + $ar = array(); + preg_match_all('/./us', $text, $ar ); + return count($ar[0]); + } + +} + +if( !function_exists( 'mb_strpos' ) ) { + + function mb_strpos( $haystack, $needle, $offset = 0 ) { + $needle = preg_quote( $needle, '/' ); + + $ar = array(); + preg_match( '/'.$needle.'/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset ); + + if( isset( $ar[0][1] ) ) { + return $ar[0][1]; + } else { + return false; + } + } + +} + +if( !function_exists( 'mb_strrpos' ) ) { + + function mb_strrpos( $haystack, $needle, $offset = 0 ) { + $needle = preg_quote( $needle, '/' ); + + $ar = array(); + preg_match_all( '/'.$needle.'/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset ); + + if( isset( $ar[0] ) && count( $ar[0] ) > 0 && + isset( $ar[0][count($ar[0])-1][1] ) ) { + return $ar[0][count($ar[0])-1][1]; + } else { + return false; + } + } +} + +if( !function_exists( 'mb_substr' ) ) { + + function mb_substr( $text, $start, $length = 0 ) { + if( $text == '' ) { return ''; } + + $ar = array(); + preg_match_all('/./us', $text, $ar ); + + $res = ''; + + if( $length == 0 ) { $length = count($ar[0]); } + + while( $length > 0 && isset( $ar[0][$start] ) ) { + $res .= $ar[0][$start]; + $start++; + $length--; + } + + return $res; + } +} Property changes on: StringFunctions.body.php ___________________________________________________________________ Added: svn:eol-style + native Index: StringFunctions.i18n.php =================================================================== --- StringFunctions.i18n.php (revision 49027) +++ StringFunctions.i18n.php (working copy) @@ -14,6 +14,7 @@ */ $messages['en'] = array( 'stringfunctions-desc' => 'Enhances the parser with string functions', + 'stringfunctions-toolong' => 'String is exceeds $1 character limit.', ); /** Message documentation (Message documentation) Index: StringFunctions.php =================================================================== --- StringFunctions.php (revision 49027) +++ StringFunctions.php (working copy) @@ -1,75 +1,82 @@ , , , and certain others. + If passed to a string function these elements are treated as having zero length. - URL-encodes the given value. - See: http://php.net/manual/function.urlencode.php + Due to the design of the parser all parameters are passed through a trim function + that removes whitespace on both ends before being recieved by the StringFunctions. + Parameters consisting entirely of whitespace will be reduced to the empty string, "". + For the purposes of operating, functions recieving an empty string will assume the + intended parameter is a single space " ". + In addition, it is possible to exploit the tag behavior described above to force the + preservation of whitespace. In particular, passing a string like: + " BOB " will be recieved as " BOB ", with the + whitespace at both ends preserved even though the tags have been stripped. - {{#urldecode:value}} + + This extension requires PHP's Perl Compatible Regular Expressions (PCRE) be available. + In addition, it makes use of PHP's multi-byte string functions if they are available. + If multi-byte string function are not available this behavior is simulated through + much slower regex. Hence the presence of multi-byte string functions is strongly + recommended. - URL-decodes the given value. - See: http://php.net/manual/function.urldecode.php + == Copyright == - Copyright (c) 2008 Ross McClure & Juraj Simlovic + Copyright (c) 2008-2009 Robert Rohde, Ross McClure, and Juraj Simlovic + http://en.wikipedia.org/wiki/User:Dragons_flight http://www.mediawiki.org/wiki/User:Algorithm http://www.mediawiki.org/wiki/User:jsimlo @@ -96,379 +103,48 @@ */ -$wgExtensionCredits['parserhook'][] = array( +$wgStringFunctionsLimit = 1000; + +$wgExtensionCredits['validextensionclass'][] = array( 'name' => 'StringFunctions', - 'version' => '2.0.3', // Nov 30, 2008 + 'version' => '3.0.0', // March 30, 2009 'description' => 'Enhances parser with string functions', 'descriptionmsg' => 'stringfunctions-desc', - 'author' => array('Ross McClure', 'Juraj Simlovic'), + 'author' => array('Robert Rohde', 'Ross McClure', 'Juraj Simlovic'), 'url' => 'http://www.mediawiki.org/wiki/Extension:StringFunctions', ); -$dir = dirname( __FILE__ ) . '/'; -$wgExtensionMessagesFiles['StringFunctions'] = $dir . 'StringFunctions.i18n.php'; +$wgAutoloadClasses['StringFunctions'] = + dirname( __FILE__ ) . '/StringFunctions.body.php'; +$wgExtensionMessagesFiles['StringFunctions'] = + dirname( __FILE__ ) . '/StringFunctions.i18n.php'; $wgExtensionFunctions[] = 'wfStringFunctions'; $wgHooks['LanguageGetMagic'][] = 'wfStringFunctionsLanguageGetMagic'; +$wgHooks['ParserAfterStrip'][] = 'StringFunctions::loadRegex'; function wfStringFunctions ( ) { - global $wgParser, $wgExtStringFunctions; - global $wgStringFunctionsLimitSearch; - global $wgStringFunctionsLimitReplace; - global $wgStringFunctionsLimitPad; + global $wgParser, $wgStringFunctions; - $wgExtStringFunctions = new ExtStringFunctions ( ); - $wgStringFunctionsLimitSearch = 30; - $wgStringFunctionsLimitReplace = 30; - $wgStringFunctionsLimitPad = 100; + $wgStringFunctions = new StringFunctions ( ); - $wgParser->setFunctionHook('len', array(&$wgExtStringFunctions,'runLen' )); - $wgParser->setFunctionHook('pos', array(&$wgExtStringFunctions,'runPos' )); - $wgParser->setFunctionHook('rpos', array(&$wgExtStringFunctions,'runRPos' )); - $wgParser->setFunctionHook('sub', array(&$wgExtStringFunctions,'runSub' )); - $wgParser->setFunctionHook('pad', array(&$wgExtStringFunctions,'runPad' )); - $wgParser->setFunctionHook('replace', array(&$wgExtStringFunctions,'runReplace' )); - $wgParser->setFunctionHook('explode', array(&$wgExtStringFunctions,'runExplode' )); - $wgParser->setFunctionHook('urlencode',array(&$wgExtStringFunctions,'runUrlEncode')); - $wgParser->setFunctionHook('urldecode',array(&$wgExtStringFunctions,'runUrlDecode')); + $wgParser->setFunctionHook('len', array(&$wgStringFunctions,'runLen' )); + $wgParser->setFunctionHook('pos', array(&$wgStringFunctions,'runPos' )); + $wgParser->setFunctionHook('rpos', array(&$wgStringFunctions,'runRPos' )); + $wgParser->setFunctionHook('sub', array(&$wgStringFunctions,'runSub' )); + $wgParser->setFunctionHook('replace', array(&$wgStringFunctions,'runReplace' )); + $wgParser->setFunctionHook('explode', array(&$wgStringFunctions,'runExplode' )); } function wfStringFunctionsLanguageGetMagic( &$magicWords, $langCode = "en" ) { - switch ( $langCode ) { - default: - $magicWords['len'] = array ( 0, 'len' ); - $magicWords['pos'] = array ( 0, 'pos' ); - $magicWords['rpos'] = array ( 0, 'rpos' ); - $magicWords['sub'] = array ( 0, 'sub' ); - $magicWords['pad'] = array ( 0, 'pad' ); - $magicWords['replace'] = array ( 0, 'replace' ); - $magicWords['explode'] = array ( 0, 'explode' ); - $magicWords['urlencode'] = array ( 0, 'urlencode' ); - $magicWords['urldecode'] = array ( 0, 'urldecode' ); - } + + $magicWords['len'] = array ( 0, 'len' ); + $magicWords['pos'] = array ( 0, 'pos' ); + $magicWords['rpos'] = array ( 0, 'rpos' ); + $magicWords['sub'] = array ( 0, 'sub' ); + $magicWords['replace'] = array ( 0, 'replace' ); + $magicWords['explode'] = array ( 0, 'explode' ); + return true; -} - -class ExtStringFunctions { - - /** - * Returns part of the perl regexp pattern that matches a marker. - * Unfortunatelly, we are still backward-supporting old versions. - */ - function mwMarkerRE ( &$parser ) - { - if ( defined('Parser::MARKER_SUFFIX') ) - $suffix = preg_quote( Parser::MARKER_SUFFIX, '/' ); - elseif ( isset($parser->mMarkerSuffix) ) - $suffix = preg_quote( $parser->mMarkerSuffix, '/' ); - elseif ( defined('MW_PARSER_VERSION') && strcmp( MW_PARSER_VERSION, '1.6.1' ) > 0 ) - $suffix = "QINU\x07"; - else $suffix = 'QINU'; - - return preg_quote( $parser->mUniqPrefix, '/' ) . '.*?' . $suffix; - } - - /** - * {{#len:value}} - * - * Main idea: Count multibytes. Find markers. Substract. - */ - function runLen ( &$parser, $inStr = '' ) { - - $len = mb_strlen ( (string)$inStr ); - - $count = preg_match_all ( - '/' . $this->mwMarkerRE($parser) . '/', - (string) $inStr, $matches - ); - - foreach ($matches[0] as $match) - $len -= strlen ($match) - 1; - - return $len; - } - - /** - * Splits the string into its component parts using preg_match_all(). - * $chars is set to the resulting array of multibyte characters. - * Returns count($chars). - */ - function mwSplit ( &$parser, $str, &$chars ) { - # Get marker prefix & suffix - $prefix = preg_quote( $parser->mUniqPrefix, '/' ); - if ( defined('Parser::MARKER_SUFFIX') ) - $suffix = preg_quote( Parser::MARKER_SUFFIX, '/' ); - elseif ( isset($parser->mMarkerSuffix) ) - $suffix = preg_quote( $parser->mMarkerSuffix, '/' ); - elseif ( defined('MW_PARSER_VERSION') && strcmp( MW_PARSER_VERSION, '1.6.1' ) > 0 ) - $suffix = "QINU\x07"; - else $suffix = 'QINU'; - - # Treat strip markers as single multibyte characters - $count = preg_match_all('/' . $prefix . '.*?' . $suffix . '|./su', $str, $arr); - $chars = $arr[0]; - return $count; - } - - /** - * {{#pos:value|key|offset}} - * Note: If the needle is an empty string, single space is used instead. - * Note: If the needle is not found, empty string is returned. - * Note: The needle is limited to specific length. - */ - function runPos ( &$parser, $inStr = '', $inNeedle = '', $inOffset = 0 ) { - global $wgStringFunctionsLimitSearch; - - if ( $inNeedle === '' ) { - # empty needle - $needle = array(' '); - $nSize = 1; - } else { - # convert needle - $nSize = $this->mwSplit ( $parser, $inNeedle, $needle ); - - if ( $nSize > $wgStringFunctionsLimitSearch ) { - $nSize = $wgStringFunctionsLimitSearch; - $needle = array_slice ( $needle, 0, $nSize ); - } - } - - # convert string - $size = $this->mwSplit( $parser, $inStr, $chars ) - $nSize; - $inOffset = max ( intval($inOffset), 0 ); - - # find needle - for ( $i = $inOffset; $i <= $size; $i++ ) { - if ( $chars[$i] !== $needle[0] ) continue; - for ( $j = 1; ; $j++ ) { - if ( $j >= $nSize ) return $i; - if ( $chars[$i + $j] !== $needle[$j] ) break; - } - } - - # return empty string upon not found - return ''; - } - - /** - * {{#rpos:value|key}} - * Note: If the needle is an empty string, single space is used instead. - * Note: If the needle is not found, -1 is returned. - * Note: The needle is limited to specific length. - */ - function runRPos ( &$parser, $inStr = '', $inNeedle = '' ) { - global $wgStringFunctionsLimitSearch; - - if ( $inNeedle === '' ) { - # empty needle - $needle = array(' '); - $nSize = 1; - } else { - # convert needle - $nSize = $this->mwSplit ( $parser, $inNeedle, $needle ); - - if ( $nSize > $wgStringFunctionsLimitSearch ) { - $nSize = $wgStringFunctionsLimitSearch; - $needle = array_slice ( $needle, 0, $nSize ); - } - } - - # convert string - $size = $this->mwSplit( $parser, $inStr, $chars ) - $nSize; - - # find needle - for ( $i = $size; $i >= 0; $i-- ) { - if ( $chars[$i] !== $needle[0] ) continue; - for ( $j = 1; ; $j++ ) { - if ( $j >= $nSize ) return $i; - if ( $chars[$i + $j] !== $needle[$j] ) break; - } - } - - # return -1 upon not found - return "-1"; - } - - /** - * {{#sub:value|start|length}} - * Note: If length is zero, the rest of the input is returned. - */ - function runSub ( &$parser, $inStr = '', $inStart = 0, $inLength = 0 ) { - # convert string - $this->mwSplit( $parser, $inStr, $chars ); - - # zero length - if ( intval($inLength) == 0 ) - return join('', array_slice( $chars, intval($inStart) )); - - # non-zero length - return join('', array_slice( $chars, intval($inStart), intval($inLength) )); - } - - /** - * {{#pad:value|length|with|direction}} - * Note: Length of the resulting string is limited. - */ - function runPad( &$parser, $inStr = '', $inLen = 0, $inWith = '', $inDirection = '' ) { - global $wgStringFunctionsLimitPad; - - # direction - switch ( strtolower ( $inDirection ) ) { - case 'center': - $direction = STR_PAD_BOTH; - break; - case 'right': - $direction = STR_PAD_RIGHT; - break; - case 'left': - default: - $direction = STR_PAD_LEFT; - break; - } - - # prevent markers in padding - $a = explode ( $parser->mUniqPrefix, $inWith, 2 ); - if ( $a[0] === '' ) - $inWith = ' '; - else $inWith = $a[0]; - - # limit pad length - $inLen = intval ( $inLen ); - if ($wgStringFunctionsLimitPad > 0) - $inLen = min ( $inLen, $wgStringFunctionsLimitPad ); - - # adjust for multibyte strings - $inLen += strlen( $inStr ) - $this->mwSplit( $parser, $inStr, $a ); - - # pad - return str_pad ( $inStr, $inLen, $inWith, $direction ); - } - - /** - * {{#replace:value|from|to}} - * Note: If the needle is an empty string, single space is used instead. - * Note: The needle is limited to specific length. - * Note: The product is limited to specific length. - */ - function runReplace( &$parser, $inStr = '', $inReplaceFrom = '', $inReplaceTo = '' ) { - global $wgStringFunctionsLimitSearch, $wgStringFunctionsLimitReplace; - - if ( $inReplaceFrom === '' ) { - # empty needle - $needle = array(' '); - $nSize = 1; - } else { - # convert needle - $nSize = $this->mwSplit ( $parser, $inReplaceFrom, $needle ); - if ( $nSize > $wgStringFunctionsLimitSearch ) { - $nSize = $wgStringFunctionsLimitSearch; - $needle = array_slice ( $needle, 0, $nSize ); - } - } - - # convert product - $pSize = $this->mwSplit ( $parser, $inReplaceTo, $product ); - if ( $pSize > $wgStringFunctionsLimitReplace ) { - $pSize = $wgStringFunctionsLimitReplace; - $product = array_slice ( $product, 0, $pSize ); - } - - # remove markers in product - for( $i = 0; $i < $pSize; $i++ ) { - if( strlen( $product[$i] ) > 6 ) $product[$i] = ' '; - } - - # convert string - $size = $this->mwSplit ( $parser, $inStr, $chars ) - $nSize; - - # replace - for ( $i = 0; $i <= $size; $i++ ) { - if ( $chars[$i] !== $needle[0] ) continue; - for ( $j = 1; ; $j++ ) { - if ( $j >= $nSize ) { - array_splice ( $chars, $i, $j, $product ); - $size += ( $pSize - $nSize ); - $i += ( $pSize - 1 ); - break; - } - if ( $chars[$i + $j] !== $needle[$j] ) break; - } - } - return join('', $chars); - } - - /** - * {{#explode:value|delimiter|position}} - * Note: Negative position can be used to specify tokens from the end. - * Note: If the divider is an empty string, single space is used instead. - * Note: The divider is limited to specific length. - * Note: Empty string is returned, if there is not enough exploded chunks. - */ - function runExplode ( &$parser, $inStr = '', $inDiv = '', $inPos = 0 ) { - global $wgStringFunctionsLimitSearch; - - if ( $inDiv === '' ) { - # empty divider - $div = array(' '); - $dSize = 1; - } else { - # convert divider - $dSize = $this->mwSplit ( $parser, $inDiv, $div ); - if ( $dSize > $wgStringFunctionsLimitSearch ) { - $dSize = $wgStringFunctionsLimitSearch; - $div = array_slice ( $div, 0, $dSize ); - } - } - - # convert string - $size = $this->mwSplit ( $parser, $inStr, $chars ) - $dSize; - - # explode - $inPos = intval ( $inPos ); - $tokens = array(); - $start = 0; - for ( $i = 0; $i <= $size; $i++ ) { - if ( $chars[$i] !== $div[0] ) continue; - for ( $j = 1; ; $j++ ) { - if ( $j >= $dSize ) { - if ( $inPos > 0 ) $inPos--; - else { - $tokens[] = join('', array_slice($chars, $start, ($i - $start))); - if ( $inPos == 0 ) return $tokens[0]; - } - $start = $i + $j; - $i = $start - 1; - break; - } - if ( $chars[$i + $j] !== $div[$j] ) break; - } - } - $tokens[] = join('', array_slice( $chars, $start )); - - # negative $inPos - if ( $inPos < 0 ) $inPos += count ( $tokens ); - - # out of range - if ( !isset ( $tokens[$inPos] ) ) return ""; - - # in range - return $tokens[$inPos]; - } - - /** - * {{#urlencode:value}} - */ - function runUrlEncode ( &$parser, $inStr = '' ) { - # encode - return urlencode ( $inStr ); - } - - /** - * {{#urldecode:value}} - */ - function runUrlDecode ( &$parser, $inStr = '' ) { - # decode - return urldecode ( $inStr ); - } -} +} \ No newline at end of file