|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Definition of eZTemplateForeachFunction class 00004 // 00005 // Created on: <24-Feb-2005 15:47:35 vs> 00006 // 00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00008 // SOFTWARE NAME: eZ Publish 00009 // SOFTWARE RELEASE: 4.0.x 00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS 00011 // SOFTWARE LICENSE: GNU General Public License v2.0 00012 // NOTICE: > 00013 // This program is free software; you can redistribute it and/or 00014 // modify it under the terms of version 2.0 of the GNU General 00015 // Public License as published by the Free Software Foundation. 00016 // 00017 // This program is distributed in the hope that it will be useful, 00018 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 // GNU General Public License for more details. 00021 // 00022 // You should have received a copy of version 2.0 of the GNU General 00023 // Public License along with this program; if not, write to the Free 00024 // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00025 // MA 02110-1301, USA. 00026 // 00027 // 00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ## 00029 // 00030 00031 /*! 00032 \class eZTemplateForeachFunction eztemplateforeachfunction.php 00033 \ingroup eZTemplateFunctions 00034 \brief FOREACH loop 00035 00036 Syntax: 00037 \code 00038 {foreach <array> as [$keyVar =>] $itemVar 00039 [sequence <array> as $sequenceVar] 00040 [offset <offset>] 00041 [max <max>] 00042 [reverse]} 00043 00044 [{delimiter}...{/delimiter}] 00045 [{break}] 00046 [{continue}] 00047 [{skip}] 00048 {/foreach} 00049 \endcode 00050 00051 Example: 00052 \code 00053 {foreach $objects as $object} 00054 <tr> 00055 {foreach $object.nodes as $node sequence array(dark,light) as $class} 00056 <td class=$class> 00057 {$node.name|wash} 00058 </td> 00059 {/foreach} 00060 </tr> 00061 {/foreach} 00062 \endcode 00063 */ 00064 00065 class eZTemplateForeachFunction 00066 { 00067 const FUNCTION_NAME = 'foreach'; 00068 00069 /*! 00070 * Returns an array of the function names, required for eZTemplate::registerFunctions(). 00071 */ 00072 function &functionList() 00073 { 00074 $functionList = array( eZTemplateForeachFunction::FUNCTION_NAME ); 00075 return $functionList; 00076 } 00077 00078 /*! 00079 * Returns the attribute list. 00080 * key: parameter name 00081 * value: can have children 00082 */ 00083 function attributeList() 00084 { 00085 return array( 'delimiter' => true, 00086 'break' => false, 00087 'continue' => false, 00088 'skip' => false ); 00089 } 00090 00091 00092 /*! 00093 * Returns the array with hits for the template compiler. 00094 */ 00095 function functionTemplateHints() 00096 { 00097 return array( eZTemplateForeachFunction::FUNCTION_NAME => array( 'parameters' => true, 00098 'static' => false, 00099 'transform-parameters' => true, 00100 'tree-transformation' => true ) ); 00101 } 00102 00103 /*! 00104 * Compiles the function and its children into PHP code. 00105 */ 00106 function templateNodeTransformation( $functionName, &$node, 00107 $tpl, $parameters, $privateData ) 00108 { 00109 /* 00110 {foreach <array> as [$keyVar =>] $itemVar 00111 [sequence <array> as $sequenceVar] 00112 [offset <offset>] 00113 [max <max>] 00114 [reverse] 00115 } 00116 */ 00117 00118 $tpl->ForeachCounter++; 00119 $newNodes = array(); 00120 $nodePlacement = eZTemplateNodeTool::extractFunctionNodePlacement( $node ); 00121 $uniqid = md5( $nodePlacement[2] ) . "_" . $tpl->ForeachCounter; 00122 00123 require_once( 'lib/eztemplate/classes/eztemplatecompiledloop.php' ); 00124 $loop = new eZTemplateCompiledLoop( eZTemplateForeachFunction::FUNCTION_NAME, 00125 $newNodes, $parameters, $nodePlacement, $uniqid, 00126 $node, $tpl, $privateData ); 00127 00128 00129 00130 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "// foreach begins" ); 00131 00132 $loop->initVars(); 00133 00134 $array = "fe_array_$uniqid"; 00135 $arrayKeys = "fe_array_keys_$uniqid"; 00136 $nItems = "fe_n_items_$uniqid"; 00137 $nItemsProcessed = "fe_n_items_processed_$uniqid"; 00138 $i = "fe_i_$uniqid"; 00139 $key = "fe_key_$uniqid"; 00140 $val = "fe_val_$uniqid"; 00141 $offset = "fe_offset_$uniqid"; 00142 $max = "fe_max_$uniqid"; 00143 $reverse = "fe_reverse_$uniqid"; 00144 $firstVal = "fe_first_val_$uniqid"; 00145 $lastVal = "fe_last_val_$uniqid"; 00146 00147 $variableStack = "fe_variable_stack_$uniqid"; 00148 $namesArray = array( $array, $arrayKeys, $nItems, $nItemsProcessed, $i, $key, $val, $offset, $max, $reverse, $firstVal, $lastVal ); 00149 00150 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "if ( !isset( \$$variableStack ) ) \$$variableStack = array();" ); 00151 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$" . $variableStack ."[] = compact( '" . implode( "', '", $namesArray ) . "' );" ); 00152 00153 $newNodes[] = eZTemplateNodeTool::createVariableNode( false, $parameters['array'], $nodePlacement, array( 'text-result' => false ), $array ); 00154 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$arrayKeys = is_array( \$$array ) ? array_keys( \$$array ) : array();" ); 00155 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$nItems = count( \$$arrayKeys );" ); 00156 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$nItemsProcessed = 0;" ); 00157 00158 00159 // process offset, max and reverse parameters 00160 if ( isset( $parameters['offset'] ) ) 00161 $newNodes[] = eZTemplateNodeTool::createVariableNode( false, $parameters['offset'], $nodePlacement, array( 'text-result' => false ), $offset ); 00162 else 00163 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$offset = 0;" ); 00164 00165 if ( isset( $parameters['max'] ) ) 00166 $newNodes[] = eZTemplateNodeTool::createVariableNode( false, $parameters['max'], $nodePlacement, array( 'text-result' => false ), $max ); 00167 else 00168 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$max = \$$nItems - \$$offset;" ); 00169 00170 if ( isset( $parameters['reverse'] ) ) 00171 $newNodes[] = eZTemplateNodeTool::createVariableNode( false, $parameters['reverse'], $nodePlacement, array( 'text-result' => false ), $reverse ); 00172 else 00173 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$reverse = false;" ); 00174 00175 00176 // fix definitely incorrect offset 00177 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "if ( \$$offset < 0 || \$$offset >= \$$nItems )\n{\n". 00178 " \$$offset = ( \$$offset < 0 ) ? 0 : \$$nItems;\n". 00179 " if ( \$$nItems || \$$offset < 0 )\n {\n". 00180 " eZDebug::writeWarning(\"Invalid 'offset' parameter specified.\"); \n}\n}" ); 00181 // fix definitely incorrect max 00182 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "if ( \$$max < 0 || \$$offset + \$$max > \$$nItems )\n{\n". 00183 " if ( \$$max < 0 )\n eZDebug::writeWarning(\"Invalid 'max' parameter specified.\");\n". 00184 " \$$max = \$$nItems - \$$offset;\n}" ); 00185 00186 // initialize first and last indexes to iterate between them 00187 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "if ( \$$reverse )\n" . 00188 "{\n" . 00189 " \$$firstVal = \$$nItems - 1 - \$$offset;\n" . 00190 " \$$lastVal = 0;\n" . 00191 "}\n" . 00192 "else\n" . 00193 "{\n" . 00194 " \$$firstVal = \$$offset;\n" . 00195 " \$$lastVal = \$$nItems - 1;\n" . 00196 "}" ); 00197 00198 // generate loop header 00199 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "// foreach" ); 00200 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "for ( \$$i = \$$firstVal; " . 00201 "\$$nItemsProcessed < \$$max && ( \$$reverse ? \$$i >= \$$lastVal : \$$i <= \$$lastVal ); " . 00202 "\$$reverse ? \$$i-- : \$$i++ )\n" . 00203 "{" ); 00204 $newNodes[] = eZTemplateNodeTool::createSpacingIncreaseNode(); 00205 00206 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$key = \$${arrayKeys}[\$$i];" ); 00207 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$val = \$${array}[\$$key];" ); 00208 00209 // export $itemVar 00210 $newNodes[] = eZTemplateNodeTool::createVariableNode( false, "$val", $nodePlacement, array(), 00211 $parameters['item_var'][0][1], 00212 false, true, true ); 00213 00214 // export $keyVar (if specified) 00215 if ( isset( $parameters['key_var'] ) ) 00216 { 00217 $newNodes[] = eZTemplateNodeTool::createVariableNode( false, "$key", $nodePlacement, array(), 00218 $parameters['key_var'][0][1], 00219 false, true, true ); 00220 } 00221 00222 $loop->processBody(); 00223 00224 // generate loop footer 00225 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "\$$nItemsProcessed++;" ); 00226 $newNodes[] = eZTemplateNodeTool::createSpacingDecreaseNode(); 00227 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "} // foreach" ); 00228 00229 $loop->cleanup(); 00230 00231 // unset the loop variables 00232 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "if ( count( \$$variableStack ) ) extract( array_pop( \$$variableStack ) );\n" ); 00233 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "else\n{\n" ); 00234 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $array ); 00235 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $arrayKeys ); 00236 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $nItems ); 00237 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $nItemsProcessed ); 00238 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $i ); 00239 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $key ); 00240 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $val ); 00241 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $offset ); 00242 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $max ); 00243 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $reverse ); 00244 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $firstVal ); 00245 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $lastVal ); 00246 00247 $newNodes[] = eZTemplateNodeTool::createVariableUnsetNode( $variableStack ); 00248 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "}\n" ); 00249 00250 $newNodes[] = eZTemplateNodeTool::createCodePieceNode( "// foreach ends" ); 00251 00252 return $newNodes; 00253 } 00254 00255 /*! 00256 * Actually executes the function and its children (in processed mode). 00257 */ 00258 function process( $tpl, &$textElements, $functionName, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace ) 00259 { 00260 /* 00261 {foreach <array> as [$keyVar =>] $itemVar 00262 [sequence <array> as $sequenceVar] 00263 [offset <offset>] 00264 [max <max>] 00265 [reverse] 00266 } 00267 */ 00268 00269 //eZDebug::writeDebug( $functionParameters, '$functionParameters' ); 00270 00271 require_once( 'lib/eztemplate/classes/eztemplateloop.php' ); 00272 $loop = new eZTemplateLoop( eZTemplateForeachFunction::FUNCTION_NAME, 00273 $functionParameters, $functionChildren, $functionPlacement, 00274 $tpl, $textElements, $rootNamespace, $currentNamespace ); 00275 00276 if ( !$loop->initialized() ) 00277 return; 00278 00279 $loop->parseParamValue( 'array', $array ); 00280 if ( !is_array( $array ) ) 00281 { 00282 $tpl->error( eZTemplateForeachFunction::FUNCTION_NAME, "Missing/malformed array to iterate through.", $functionPlacement ); 00283 return; 00284 } 00285 00286 $loop->parseParamVarName( 'item_var', $itemVarName ); 00287 if ( !$itemVarName ) 00288 { 00289 $tpl->error( eZTemplateForeachFunction::FUNCTION_NAME, "Missing/malformed item variable name.", $functionPlacement ); 00290 return; 00291 } 00292 00293 $loop->parseParamVarName( 'key_var', $keyVarName ); 00294 $loop->parseParamValue( 'max', $max ); 00295 $loop->parseParamValue( 'offset', $offset ); 00296 $loop->parseParamValue( 'reverse', $reverse ); 00297 00298 /* 00299 * run the loop itself 00300 */ 00301 00302 /* 00303 $offset and $max parameters must meet the following requirements: 00304 - $offset + $max <= $nItems 00305 - $offset >= 0 00306 - $max >= 0 00307 Otherwise they are not considered. 00308 */ 00309 00310 $arrayKeys = array_keys( $array ); 00311 $nItems = count( $arrayKeys ); 00312 $nItemsProcessed = 0; 00313 00314 // do nothing in case of empty array 00315 if ( !$nItems ) 00316 return; 00317 00318 // fix definitely incorrect offset 00319 if ( is_null( $offset ) ) 00320 $offset = 0; 00321 elseif ( $offset < 0 || $offset >= $nItems ) 00322 { 00323 if ( $nItems || $offset < 0 ) 00324 { 00325 $tpl->warning( eZTemplateForeachFunction::FUNCTION_NAME, "Invalid 'offset' parameter specified.", $functionPlacement ); 00326 } 00327 $offset = ( $offset < 0 ) ? 0 : $nItems; 00328 } 00329 00330 // fix definitely incorrect max 00331 if ( is_null( $max ) ) 00332 $max = $nItems - $offset; 00333 elseif ( $max < 0 || $offset+$max > $nItems ) 00334 { 00335 if ( $max <0 ) 00336 $tpl->warning( eZTemplateForeachFunction::FUNCTION_NAME, "Invalid 'max' parameter specified.", $functionPlacement ); 00337 $max = $nItems - $offset; 00338 } 00339 00340 // process 'reverse' parameter 00341 if ( is_null( $reverse ) ) 00342 $reverse = false; 00343 if ( $reverse ) 00344 { 00345 $firstVal = $nItems - 1 - $offset; 00346 $lastVal = 0; 00347 } 00348 else 00349 { 00350 $firstVal = $offset; 00351 $lastVal = $nItems - 1; 00352 } 00353 00354 if ( $firstVal < $lastVal ) 00355 { 00356 if ( $keyVarName && !$tpl->hasVariable( $keyVarName, $rootNamespace ) ) 00357 { 00358 $loop->initLoopVariable( $keyVarName ); 00359 } 00360 if ( !$tpl->hasVariable( $itemVarName, $rootNamespace ) ) 00361 { 00362 $loop->initLoopVariable( $itemVarName ); 00363 } 00364 } 00365 00366 for ( $i = $firstVal; $nItemsProcessed < $max && ( $reverse ? $i >= $lastVal : $i <= $lastVal ); ) 00367 { 00368 $key =& $arrayKeys[$i]; 00369 $val =& $array[$key]; 00370 00371 if ( $keyVarName ) 00372 $tpl->setVariable( $keyVarName, $key, $rootNamespace ); 00373 $tpl->setVariable( $itemVarName, $val, $rootNamespace ); 00374 00375 $loop->setSequenceVar(); // set sequence variable (if specified) 00376 $loop->processDelimiter( $i ); 00377 $loop->resetIteration(); 00378 00379 // process loop body 00380 if ( $loop->processChildren() ) 00381 break; 00382 00383 // increment loop counter here for delimiter to be processed correctly 00384 $reverse ? $i-- : $i++; 00385 00386 $loop->incrementSequence(); 00387 $nItemsProcessed++; 00388 } 00389 00390 $loop->cleanup(); 00391 } 00392 00393 /*! 00394 * Returns true, telling the template parser that the function can have children. 00395 */ 00396 function hasChildren() 00397 { 00398 return true; 00399 } 00400 } 00401 00402 ?>