|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Definition of eZCodeTemplate class 00004 // 00005 // Created on: <18-Nov-2004 13:03:44 jb> 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 /*! \file ezcodetemplate.php 00032 */ 00033 00034 /*! 00035 \class eZCodeTemplate ezcodetemplate.php 00036 \brief Replaces or generates blocks of code according to a template file 00037 00038 */ 00039 00040 class eZCodeTemplate 00041 { 00042 /// There are errors in the template code 00043 const STATUS_FAILED = 0; 00044 00045 /// Code files was succesfully updated 00046 const STATUS_OK = 1; 00047 00048 /// Code file was updated, but no new elements has been added 00049 const STATUS_NO_CHANGE = 2; 00050 00051 /*! 00052 Constructor 00053 */ 00054 function eZCodeTemplate() 00055 { 00056 $ini = eZINI::instance( 'codetemplate.ini' ); 00057 $this->Templates = array(); 00058 $templates = $ini->variable( 'Files', 'Templates' ); 00059 foreach ( $templates as $key => $template ) 00060 { 00061 $this->Templates[$key] = array( 'filepath' => $template ); 00062 } 00063 } 00064 00065 /*! 00066 Applies template block in the file \a $filePath and writes back the new 00067 code to the same file. 00068 00069 \return One of the EZ_CODE_TEMPLATE_STATUS_* status codes. 00070 00071 \note It will create a backup file of the original 00072 */ 00073 function apply( $filePath, $checkOnly = false ) 00074 { 00075 if ( !file_exists( $filePath ) ) 00076 { 00077 eZDebug::writeError( "File $filePath does not exists", 00078 'eZCodeTemplate::apply' ); 00079 return self::STATUS_FAILED; 00080 } 00081 00082 $text = file_get_contents( $filePath ); 00083 $tempFile = dirname( $filePath ) . '/#' . basename( $filePath ) . '#'; 00084 $fd = fopen( $tempFile, 'wb' ); 00085 if ( !$fd ) 00086 { 00087 eZDebug::writeError( "Failed to open temporary file $tempFile", 00088 'eZCodeTemplate::apply' ); 00089 return self::STATUS_FAILED; 00090 } 00091 00092 $createTag = 'code-template::create-block:'; 00093 $createTagLen = strlen( $createTag ); 00094 00095 $error = false; 00096 00097 $ok = true; 00098 $offset = 0; 00099 $len = strlen( $text ); 00100 while ( $offset < $len ) 00101 { 00102 $createPos = strpos( $text, $createTag, $offset ); 00103 if ( $createPos !== false ) 00104 { 00105 $endPos = strpos( $text, "\n", $createPos + $createTagLen ); 00106 if ( $endPos === false ) 00107 { 00108 $createText = substr( $text, $createPos + $createTagLen ); 00109 $end = $len; 00110 } 00111 else 00112 { 00113 $start = $createPos + $createTagLen; 00114 $createText = substr( $text, $start, $endPos - $start ); 00115 $end = $endPos + 1; 00116 } 00117 00118 // Figure out how much the comments should be indented 00119 // This just makes the code seem more natural 00120 $indentText = ''; 00121 $subText = substr( $text, $offset, $createPos - $offset ); 00122 $startOfLine = strrpos( $subText, "\n" ); 00123 if ( $startOfLine !== false ) 00124 { 00125 if ( preg_match( '#^([ \t]+)#', substr( $subText, $startOfLine + 1 ), $matches ) ) 00126 { 00127 $indentText = $matches[1]; 00128 } 00129 } 00130 unset( $subText ); 00131 00132 // Figure out template name and parameters 00133 $createText = trim( $createText ); 00134 $elements = explode( ',', $createText ); 00135 if ( count( $elements ) < 1 ) 00136 { 00137 eZDebug::writeError( "No template name found in file $filePath at offset $offset", 00138 'eZCodeTemplate::apply' ); 00139 $offset = $end; 00140 $error = true; 00141 continue; 00142 } 00143 00144 $templateName = trim( $elements[0] ); 00145 00146 $templateFile = $this->templateFile( $templateName ); 00147 if ( $templateFile === false ) 00148 { 00149 eZDebug::writeError( "No template file for template $templateName used in file $filePath at offset $offset", 00150 'eZCodeTemplate::apply' ); 00151 $offset = $end; 00152 $error = true; 00153 continue; 00154 } 00155 00156 if ( !file_exists( $templateFile ) ) 00157 { 00158 eZDebug::writeError( "Template file $templateFile for template $templateName does not exist", 00159 'eZCodeTemplate::apply' ); 00160 $offset = $end; 00161 $error = true; 00162 continue; 00163 } 00164 00165 $parameters = array_splice( $elements, 1 ); 00166 foreach ( $parameters as $key => $parameter ) 00167 { 00168 $parameters[$key] = trim( $parameter ); 00169 } 00170 00171 if ( !file_exists( $templateFile ) ) 00172 { 00173 eZDebug::writeError( "Template file $templateFile was not found while workin on $filePath at offset $offset", 00174 'eZCodeTemplate::apply' ); 00175 $offset = $end; 00176 $error = true; 00177 continue; 00178 } 00179 00180 // Load the template file and split it into the blocks 00181 // available blocks in the file 00182 $templateText = file_get_contents( $templateFile ); 00183 00184 $tagSplit = '#((?:<|/\*)(?:START|END):code-template::(?:[a-zA-Z]+[a-zA-Z0-9_|&-]*)(?:>|\*/)[\n]?)#'; 00185 $tagRegexp = '#(?:<|/\*)(START|END):code-template::([a-zA-Z]+[a-zA-Z0-9_|&-]*)[\n]?(?:>|\*/)#'; 00186 00187 $split = preg_split( $tagSplit, $templateText, -1, PREG_SPLIT_DELIM_CAPTURE ); 00188 00189 $currentBlocks = array(); 00190 $blocks = array(); 00191 $currentTag = false; 00192 for ( $i = 0; $i < count( $split ); ++$i ) 00193 { 00194 $part = $split[$i]; 00195 if ( ( $i % 2 ) == 1 ) 00196 { 00197 // The tag element 00198 if ( $currentTag === false ) 00199 { 00200 preg_match( $tagRegexp, trim( $part ), $matches ); 00201 $currentTag = $matches[2]; 00202 if ( $matches[1] == 'END' ) 00203 { 00204 eZDebug::writeError( "Tag $currentTag was finished before it was started, skipping it", 00205 'eZCodeTemplate::apply' ); 00206 $currentTag = false; 00207 $error = true; 00208 } 00209 else 00210 { 00211 if ( count( $currentBlocks ) > 0 ) 00212 $blocks[] = array( 'blocks' => $currentBlocks ); 00213 $currentBlocks = array(); 00214 } 00215 } 00216 else 00217 { 00218 preg_match( $tagRegexp, trim( $part ), $matches ); 00219 $tag = $matches[2]; 00220 if ( $matches[1] == 'END' ) 00221 { 00222 if ( $tag == $currentTag ) 00223 { 00224 if ( count( $currentBlocks ) > 0 ) 00225 $blocks[] = array( 'tag' => $currentTag, 00226 'blocks' => $currentBlocks ); 00227 $currentTag = false; 00228 $currentBlocks = array(); 00229 } 00230 else 00231 { 00232 eZDebug::writeError( "End tag $tag does not match start tag $currentTag, skipping it", 00233 'eZCodeTemplate::apply' ); 00234 $error = true; 00235 } 00236 } 00237 else 00238 { 00239 eZDebug::writeError( "Start tag $tag found while $currentTag is active, skipping it", 00240 'eZCodeTemplate::apply' ); 00241 $error = true; 00242 } 00243 } 00244 } 00245 else 00246 { 00247 // Plain text 00248 $currentBlocks[] = $part; 00249 } 00250 } 00251 if ( $currentTag === false ) 00252 { 00253 if ( count( $currentBlocks ) > 0 ) 00254 $blocks[] = array( 'blocks' => $currentBlocks ); 00255 } 00256 else 00257 { 00258 if ( count( $currentBlocks ) > 0 ) 00259 $blocks[] = array( 'tag' => $currentTag, 00260 'blocks' => $currentBlocks ); 00261 } 00262 00263 // Build new code with blocks to include 00264 $resultText = ''; 00265 foreach ( $blocks as $block ) 00266 { 00267 if ( isset( $block['tag'] ) ) 00268 { 00269 $tagText = $block['tag']; 00270 if ( strpos( $tagText, '&' ) !== false ) 00271 { 00272 $tags = explode( '&', $tagText ); 00273 // Check if all tags are present in parameters (and match) 00274 if ( count( array_intersect( $parameters, $tags ) ) == count( $tags ) ) 00275 { 00276 $resultText .= implode( '', $block['blocks'] ); 00277 } 00278 } 00279 else if ( strpos( $tagText, '|' ) !== false ) 00280 { 00281 $tags = explode( '|', $tagText ); 00282 // Check if at least one tag is present in parameters (or match) 00283 if ( count( array_intersect( $parameters, $tags ) ) == count( $tags ) ) 00284 { 00285 $resultText .= implode( '', $block['blocks'] ); 00286 } 00287 } 00288 else 00289 { 00290 if ( in_array( $tagText, $parameters ) ) 00291 { 00292 $resultText .= implode( '', $block['blocks'] ); 00293 } 00294 } 00295 } 00296 else 00297 { 00298 $resultText .= implode( '', $block['blocks'] ); 00299 } 00300 } 00301 00302 // Remove any end-of-line whitespaces unless keep-whitespace is used 00303 if ( !in_array( 'keep-whitespace', $parameters ) ) 00304 $resultText = preg_replace( '#[ \t]+$#m', '', $resultText ); 00305 00306 // Output text before the template-block 00307 fwrite( $fd, substr( $text, $offset, $createPos - $offset ) ); 00308 fwrite( $fd, substr( $text, $createPos, $end - $createPos ) ); 00309 00310 $offset = $end; 00311 00312 // Remove any existing auto-generated code 00313 $autogenRegexp = '#^[ \t]*// code-template::auto-generated:START ' . $templateName . '.+[ \t]*// code-template::auto-generated:END ' . $templateName . '\n#ms'; 00314 $postText = substr( $text, $offset ); 00315 $postText = preg_replace( $autogenRegexp, '', $postText ); 00316 $text = substr( $text, 0, $offset ) . $postText; 00317 unset( $postText ); 00318 00319 // Output the template code with markers 00320 fwrite( $fd, ( "$indentText// code-template::auto-generated:START $templateName\n" . 00321 "$indentText// This code is automatically generated from $templateFile\n" . 00322 "$indentText// DO NOT EDIT THIS CODE DIRECTLY, CHANGE THE TEMPLATE FILE INSTEAD\n" . 00323 "\n" ) ); 00324 00325 fwrite( $fd, $resultText ); 00326 fwrite( $fd, ( "\n$indentText// This code is automatically generated from $templateFile\n" . 00327 "$indentText// code-template::auto-generated:END $templateName\n" ) ); 00328 00329 } 00330 else 00331 { 00332 fwrite( $fd, substr( $text, $offset ) ); 00333 break; 00334 } 00335 } 00336 00337 fclose( $fd ); 00338 if ( !$error ) 00339 { 00340 $originalMD5 = md5_file( $filePath ); 00341 $updatedMD5 = md5_file( $tempFile ); 00342 if ( $originalMD5 == $updatedMD5 ) 00343 { 00344 unlink( $tempFile ); 00345 return self::STATUS_NO_CHANGE; 00346 } 00347 else if ( $checkOnly ) 00348 { 00349 unlink( $tempFile ); 00350 return self::STATUS_OK; 00351 } 00352 else 00353 { 00354 $backupFile = $filePath . eZSys::backupFilename(); 00355 // Make a backup and make the temporary file the real one 00356 if ( file_exists( $backupFile ) ) 00357 unlink( $backupFile ); 00358 rename( $filePath, $backupFile ); 00359 rename( $tempFile, $filePath ); 00360 return self::STATUS_OK; 00361 } 00362 } 00363 unlink( $tempFile ); 00364 return self::STATUS_FAILED; 00365 } 00366 00367 /*! 00368 \return The name of the template file based on the name \a $templateName 00369 or \c false if no file is defined for the name. 00370 */ 00371 function templateFile( $templateName ) 00372 { 00373 if ( isset( $this->Templates[$templateName] ) ) 00374 { 00375 return $this->Templates[$templateName]['filepath']; 00376 } 00377 return false; 00378 } 00379 00380 /*! 00381 \static 00382 Finds all PHP files which must be updated and returns them as an array. 00383 00384 The files are defined in \c codetemplate.ini in the variable \c PHPFiles 00385 */ 00386 function allCodeFiles() 00387 { 00388 $ini = eZINI::instance( 'codetemplate.ini' ); 00389 return $ini->variable( 'Files', 'PHPFiles' ); 00390 } 00391 00392 /// \privatesection 00393 public $Templates; 00394 } 00395 00396 ?>