eZ Publish  [trunk]
ezphpcreator.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZPHPCreator class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package lib
00009  */
00010 
00011 /*!
00012   \class eZPHPCreator ezphpcreator.php
00013   \ingroup eZUtils
00014   \brief eZPHPCreator provides a simple interface for creating and executing PHP code.
00015 
00016   To create PHP code you must create an instance of this class,
00017   add any number of elements you choose with
00018   addDefine(), addVariable(), addVariableUnset(), addVariableUnsetList(),
00019   addSpace(), addText(), addMethodCall(), addCodePiece(), addComment() and
00020   addInclude().
00021   After that you call store() to write all changes to disk.
00022 
00023 \code
00024 $php = new eZPHPCreator( 'cache', 'code.php' );
00025 
00026 $php->addComment( 'Auto generated' );
00027 $php->addInclude( 'inc.php' );
00028 $php->addVariable( 'count', 10 );
00029 
00030 $php->store();
00031 \endcode
00032 
00033   To restore PHP code you must create an instance of this class,
00034   check if you can restore it with canRestore() then restore variables with restore().
00035   The class will include PHP file and run all code, once the file is done it will
00036   catch any variables you require and return it to you.
00037 
00038 \code
00039 $php = new eZPHPCreator( 'cache', 'code.php' );
00040 
00041 if ( $php->canRestore() )
00042 {
00043     $variables = $php->restore( array( 'max_count' => 'count' ) );
00044     print( "Max count was " . $variables['max_count'] );
00045 }
00046 
00047 $php->close();
00048 \endcode
00049 
00050 */
00051 
00052 class eZPHPCreator
00053 {
00054     const VARIABLE = 1;
00055     const SPACE = 2;
00056     const TEXT = 3;
00057     const METHOD_CALL = 4;
00058     const CODE_PIECE = 5;
00059     const EOL_COMMENT = 6;
00060     const INCLUDE_STATEMENT = 7;
00061     const VARIABLE_UNSET = 8;
00062     const DEFINE = 9;
00063     const VARIABLE_UNSET_LIST = 10;
00064     const RAW_VARIABLE = 11;
00065 
00066     const VARIABLE_ASSIGNMENT = 1;
00067     const VARIABLE_APPEND_TEXT = 2;
00068     const VARIABLE_APPEND_ELEMENT = 3;
00069 
00070     const INCLUDE_ONCE_STATEMENT = 1;
00071     const INCLUDE_ALWAYS_STATEMENT = 2;
00072 
00073     const METHOD_CALL_PARAMETER_VALUE = 1;
00074     const METHOD_CALL_PARAMETER_VARIABLE = 2;
00075 
00076     /*!
00077      Initializes the creator with the directory path \a $dir and filename \a $file.
00078     */
00079     function eZPHPCreator( $dir, $file, $prefix = '', $options = array() )
00080     {
00081         $this->PHPDir = $dir;
00082         $this->PHPFile = $file;
00083         $this->FilePrefix = $prefix;
00084         $this->FileResource = false;
00085         $this->Elements = array();
00086         $this->TextChunks = array();
00087         $this->TemporaryCounter = 0;
00088         if ( isset( $options['spacing'] ) and ( $options['spacing'] == 'disabled') )
00089         {
00090             $this->Spacing = false;
00091         }
00092         else
00093         {
00094             $this->Spacing = true;
00095         }
00096 
00097         if ( isset( $options['clustering'] ) )
00098         {
00099             $this->ClusteringEnabled = true;
00100             $this->ClusterFileScope = $options['clustering'];
00101         }
00102         else
00103         {
00104             $this->ClusteringEnabled = false;
00105         }
00106         $this->ClusterHandler = null;
00107     }
00108 
00109     //@{
00110 
00111     /*!
00112      Adds a new define statement to the code with the name \a $name and value \a $value.
00113      The parameter \a $caseSensitive determines if the define should be made case sensitive or not.
00114 
00115      Example:
00116      \code
00117 $php->addDefine( 'MY_CONSTANT', 5 );
00118      \endcode
00119 
00120      Would result in the PHP code.
00121 
00122      \code
00123 define( 'MY_CONSTANT', 5 );
00124      \endcode
00125 
00126      \param $parameters Optional parameters, can be any of the following:
00127             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00128 
00129      \note \a $name must start with a letter or underscore, followed by any number of letters, numbers, or underscores.
00130            See http://php.net/manual/en/language.constants.php for more information.
00131      \sa http://php.net/manual/en/function.define.php
00132     */
00133     function addDefine( $name, $value, $caseSensitive = true, $parameters = array() )
00134     {
00135         $element = array( eZPHPCreator::DEFINE,
00136                           $name,
00137                           $value,
00138                           $caseSensitive,
00139                           $parameters );
00140         $this->Elements[] = $element;
00141     }
00142 
00143     /*!
00144      Adds a new raw variable tothe code with the name \a $name and value \a $value.
00145 
00146      Example:
00147      \code
00148 $php->addVariable( 'TransLationRoot', $cache['root'] );
00149      \endcode
00150 
00151      This function makes use of PHP's var_export() function which is optimized
00152      for this task.
00153     */
00154     function addRawVariable( $name, $value )
00155     {
00156         $element = array( eZPHPCreator::RAW_VARIABLE, $name, $value );
00157         $this->Elements[] = $element;
00158     }
00159 
00160     /*!
00161      Adds a new variable to the code with the name \a $name and value \a $value.
00162 
00163      Example:
00164      \code
00165 $php->addVariable( 'offset', 5  );
00166 $php->addVariable( 'text', 'some more text', eZPHPCreator::VARIABLE_APPEND_TEXT );
00167 $php->addVariable( 'array', 42, eZPHPCreator::VARIABLE_APPEND_ELEMENT );
00168      \endcode
00169 
00170      Would result in the PHP code.
00171 
00172      \code
00173 $offset = 5;
00174 $text .= 'some more text';
00175 $array[] = 42;
00176      \endcode
00177 
00178      \param $assignmentType Controls the way the value is assigned, choose one of the following:
00179             - \b eZPHPCreator::VARIABLE_ASSIGNMENT, assign using \c = (default)
00180             - \b eZPHPCreator::VARIABLE_APPEND_TEXT, append using text concat operator \c .
00181             - \b eZPHPCreator::VARIABLE_APPEND_ELEMENT, append element to array using append operator \c []
00182      \param $parameters Optional parameters, can be any of the following:
00183             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00184             - \a full-tree, Whether to displays array values as one large expression (\c true) or
00185                             split it up into multiple variables (\c false)
00186 
00187     */
00188     function addVariable( $name, $value, $assignmentType = eZPHPCreator::VARIABLE_ASSIGNMENT,
00189                           $parameters = array() )
00190     {
00191         $element = array( eZPHPCreator::VARIABLE,
00192                           $name,
00193                           $value,
00194                           $assignmentType,
00195                           $parameters );
00196         $this->Elements[] = $element;
00197     }
00198 
00199     /*!
00200      Adds code to unset a variable with the name \a $name.
00201 
00202      Example:
00203      \code
00204 $php->addVariableUnset( 'offset' );
00205      \endcode
00206 
00207      Would result in the PHP code.
00208 
00209      \code
00210 unset( $offset );
00211      \endcode
00212 
00213      \param $parameters Optional parameters, can be any of the following:
00214             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00215 
00216      \sa http://php.net/manual/en/function.unset.php
00217     */
00218     function addVariableUnset( $name,
00219                                $parameters = array() )
00220     {
00221         $element = array( eZPHPCreator::VARIABLE_UNSET,
00222                           $name,
00223                           $parameters );
00224         $this->Elements[] = $element;
00225     }
00226 
00227     /*!
00228      Adds code to unset a list of variables with name from \a $list.
00229 
00230      Example:
00231      \code
00232 $php->addVariableUnsetList( array ( 'var1', 'var2' ) );
00233      \endcode
00234 
00235      Would result in the PHP code.
00236 
00237      \code
00238 unset( $var1, $var2 );
00239      \endcode
00240 
00241      \param $parameters Optional parameters, can be any of the following:
00242             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00243 
00244      \sa http://php.net/manual/en/function.unset.php
00245     */
00246     function addVariableUnsetList( $list,
00247                                $parameters = array() )
00248     {
00249         $element = array( eZPHPCreator::VARIABLE_UNSET_LIST,
00250                           $list,
00251                           $parameters );
00252         $this->Elements[] = $element;
00253     }
00254 
00255     /*!
00256      Adds some space to the code in form of newlines. The number of lines
00257      is controlled with \a $lines which is \c 1 by default.
00258      You can use this to get more readable PHP code.
00259 
00260      Example:
00261      \code
00262 $php->addSpace( 1 );
00263      \endcode
00264     */
00265     function addSpace( $lines = 1 )
00266     {
00267         $element = array( eZPHPCreator::SPACE,
00268                           $lines );
00269         $this->Elements[] = $element;
00270     }
00271 
00272     /*!
00273      Adds some plain text to the code. The text will be placed
00274      outside of PHP start and end markers and will in principle
00275      work as printing the text.
00276 
00277      Example:
00278      \code
00279 $php->addText( 'Print me!' );
00280      \endcode
00281     */
00282     function addText( $text )
00283     {
00284         $element = array( eZPHPCreator::TEXT,
00285                           $text );
00286         $this->Elements[] = $element;
00287     }
00288 
00289     /*!
00290      Adds code to call the method \a $methodName on the object named \a $objectName,
00291      \a $methodParameters should be an array with parameter entries where each entry contains:
00292      - \a 0, The parameter value
00293      - \a 1 (\em optional), The type of parameter, is one of:
00294        - \b eZPHPCreator::METHOD_CALL_PARAMETER_VALUE, Use value directly (default if this entry is missing)
00295        - \b eZPHPCreator::METHOD_CALL_PARAMETER_VARIABLE, Use value as the name of the variable.
00296 
00297      Optionally the \a $returnValue parameter can be used to decide what should be done
00298      with the return value of the method call. It can either be \c false which means
00299      to do nothing or an array with the following entries.
00300      - \a 0, The name of the variable to assign the value to
00301      - \a 1 (\em optional), The type of assignment, uses the same value as addVariable().
00302 
00303      \param $parameters Optional parameters, can be any of the following:
00304             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00305 
00306      Example:
00307      \code
00308 $php->addMethodCall( 'node', 'name', array(), array( 'name' ) );
00309 $php->addMethodCall( 'php', 'addMethodCall',
00310                      array( array( 'node' ), array( 'name' ) ) );
00311      \endcode
00312 
00313      Would result in the PHP code.
00314 
00315      \code
00316 $name = $node->name();
00317 $php->addMethodCall( 'node', 'name' );
00318      \endcode
00319     */
00320     function addMethodCall( $objectName, $methodName, $methodParameters, $returnValue = false, $parameters = array() )
00321     {
00322         $element = array( eZPHPCreator::METHOD_CALL,
00323                           $objectName,
00324                           $methodName,
00325                           $methodParameters,
00326                           $returnValue,
00327                           $parameters );
00328         $this->Elements[] = $element;
00329     }
00330 
00331     /*!
00332      Adds custom PHP code to the file, you should only use this a last resort if any
00333      of the other \em add functions done give you the required result.
00334 
00335      \param $code Contains the code as text, the text will not be modified (except for spacing).
00336                   This means that each expression must be ended with a newline even if it's just one.
00337      \param $parameters Optional parameters, can be any of the following:
00338             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00339 
00340      Example:
00341      \code
00342 $php->addCodePiece( "if ( \$value > 2 )\n{\n    \$value = 2;\n}\n" );
00343      \endcode
00344 
00345      Would result in the PHP code.
00346 
00347      \code
00348 if ( $value > 2 )
00349 {
00350     $value = 2;
00351 }
00352      \endcode
00353 
00354     */
00355     function addCodePiece( $code, $parameters = array() )
00356     {
00357         $element = array( eZPHPCreator::CODE_PIECE,
00358                           $code,
00359                           $parameters );
00360         $this->Elements[] = $element;
00361     }
00362 
00363     /*!
00364      Adds a comment to the code, the comment will be display using multiple end-of-line
00365      comments (//), one for each newline in the text \a $comment.
00366 
00367      \param $eol Whether to add a newline at the last comment line
00368      \param $whitespaceHandling Whether to remove trailing whitespace from each line
00369      \param $parameters Optional parameters, can be any of the following:
00370             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00371 
00372      Example:
00373      \code
00374 $php->addComment( "This file is auto generated\nDo not edit!" );
00375      \endcode
00376 
00377      Would result in the PHP code.
00378 
00379      \code
00380 // This file is auto generated
00381 // Do not edit!
00382      \endcode
00383 
00384     */
00385     function addComment( $comment, $eol = true, $whitespaceHandling = true, $parameters = array() )
00386     {
00387         $element = array( eZPHPCreator::EOL_COMMENT,
00388                           $comment,
00389                           array_merge( $parameters,
00390                                        array( 'eol' => $eol,
00391                                               'whitespace-handling' => $whitespaceHandling ) ) );
00392         $this->Elements[] = $element;
00393     }
00394 
00395     /*!
00396      Adds an include statement to the code, the file to include is \a $file.
00397 
00398      \param $type What type of include statement to use, can be one of the following:
00399                   - \b eZPHPCreator::INCLUDE_ONCE_STATEMENT, use \em include_once()
00400                   - \b eZPHPCreator::INCLUDE_ALWAYS_STATEMENT, use \em include()
00401      \param $parameters Optional parameters, can be any of the following:
00402             - \a spacing, The number of spaces to place before each code line, default is \c 0.
00403 
00404      Example:
00405      \code
00406 $php->addInclude( 'lib/ezutils/classes/ezphpcreator.php' );
00407      \endcode
00408 
00409      Would result in the PHP code.
00410 
00411      \code
00412 //include_once( 'lib/ezutils/classes/ezphpcreator.php' );
00413      \endcode
00414 
00415     */
00416     function addInclude( $file, $type = eZPHPCreator::INCLUDE_ONCE_STATEMENT, $parameters = array() )
00417     {
00418         $element = array( eZPHPCreator::INCLUDE_STATEMENT,
00419                           $file,
00420                           $type,
00421                           $parameters );
00422         $this->Elements[] = $element;
00423     }
00424 
00425     //@}
00426 
00427     /*!
00428      \static
00429      Creates a variable statement with an assignment type and returns it.
00430      \param $variableName The name of the variable
00431      \param $assignmentType What kind of assignment to use, is one of the following;
00432                             - \b eZPHPCreator::VARIABLE_ASSIGNMENT, assign using \c =
00433                             - \b eZPHPCreator::VARIABLE_APPEND_TEXT, append to text using \c .
00434                             - \b eZPHPCreator::VARIABLE_APPEND_ELEMENT, append to array using \c []
00435      \param $variableParameters Optional parameters for the statement
00436     */
00437     static function variableNameText( $variableName, $assignmentType, $variableParameters = array() )
00438     {
00439         $text = '$' . $variableName;
00440         if ( $assignmentType == eZPHPCreator::VARIABLE_ASSIGNMENT )
00441         {
00442             $text .= ' = ';
00443         }
00444         else if ( $assignmentType == eZPHPCreator::VARIABLE_APPEND_TEXT )
00445         {
00446             $text .= ' .= ';
00447         }
00448         else if ( $assignmentType == eZPHPCreator::VARIABLE_APPEND_ELEMENT )
00449         {
00450             $text .= '[] = ';
00451         }
00452         return $text;
00453     }
00454 
00455     /*!
00456      Creates a text representation of the value \a $value which can
00457      be placed in files and be read back by a PHP parser as it was.
00458      The type of the values determines the output, it can be one of the following.
00459      - boolean, becomes \c true or \c false
00460      - null, becomes \c null
00461      - string, adds \ (backslash) to backslashes, double quotes, dollar signs and newlines.
00462                Then wraps the whole string in " (double quotes).
00463      - numeric, displays the value as-is.
00464      - array, expands all value recursively using this function
00465      - object, creates a representation of an object creation if the object has \c serializeData implemented.
00466 
00467      \param $column Determines the starting column in which the text will be placed.
00468                     This is used for expanding arrays and objects which can span multiple lines.
00469      \param $iteration The current iteration, starts at 0 and increases with 1 for each recursive call
00470      \param $maxIterations The maximum number of iterations to allow, if the iteration
00471                            exceeds this the array or object will be split into multiple variables.
00472                            Can be set to \c false to the array or object as-is.
00473 
00474      \note This function can be called statically if \a $maxIterations is set to \c false
00475     */
00476     function thisVariableText( $value, $column = 0, $iteration = 0, $maxIterations = 2 )
00477     {
00478         if ( isset( $this->Spacing ) and !$this->Spacing )
00479         {
00480             return var_export( $value, true );
00481         }
00482 
00483         if ( $value === true  )
00484             $text = 'true';
00485         else if ( $value === false )
00486             $text = 'false';
00487         else if ( $value === null )
00488             $text = 'null';
00489         else if ( is_string( $value ) )
00490         {
00491             $valueText = str_replace( array( "\\",
00492                                              "\"",
00493                                              "\$",
00494                                              "\n" ),
00495                                       array( "\\\\",
00496                                              "\\\"",
00497                                              "\\$",
00498                                              "\\n" ),
00499                                       $value );
00500             $text = "\"$valueText\"";
00501         }
00502         else if ( is_numeric( $value ) )
00503             $text = $value;
00504         else if ( is_object( $value ) )
00505         {
00506             if ( $maxIterations !== false and
00507                  $iteration > $maxIterations )
00508             {
00509                 $temporaryVariableName = $this->temporaryVariableName( 'obj' );
00510                 $this->writeVariable( $temporaryVariableName, $value );
00511                 $text = '$' . $temporaryVariableName;
00512             }
00513             else
00514             {
00515                 $text = '';
00516                 if ( method_exists( $value, 'serializeData' ) )
00517                 {
00518                     $serializeData = $value->serializeData();
00519                     $className = $serializeData['class_name'];
00520                     $text = "new $className(";
00521 
00522                     $column += strlen( $text );
00523                     $parameters = $serializeData['parameters'];
00524                     $variables = $serializeData['variables'];
00525 
00526                     $i = 0;
00527                     foreach ( $parameters as $parameter )
00528                     {
00529                         if ( $i > 0 )
00530                         {
00531                             $text .= ",\n" . str_repeat( ' ', $column );
00532                         }
00533                         $variableName = $variables[$parameter];
00534                         $variableValue = $value->$variableName;
00535                         $keyText = " ";
00536 
00537                         $text .= $keyText . $this->thisVariableText( $variableValue, $column + strlen( $keyText  ), $iteration + 1, $maxIterations );
00538 
00539                         ++$i;
00540                     }
00541                     if ( $i > 0 )
00542                         $text .= ' ';
00543 
00544                     $text .= ')';
00545                 }
00546             }
00547         }
00548         else if ( is_array( $value ) )
00549         {
00550             if ( $maxIterations !== false and
00551                  $iteration > $maxIterations )
00552             {
00553                 $temporaryVariableName = $this->temporaryVariableName( 'arr' );
00554                 $this->writeVariable( $temporaryVariableName, $value );
00555                 $text = '$' . $temporaryVariableName;
00556             }
00557             else
00558             {
00559                 $text = 'array(';
00560                 $column += strlen( $text );
00561                 $valueKeys = array_keys( $value );
00562                 $isIndexed = true;
00563                 for ( $i = 0; $i < count( $valueKeys ); ++$i )
00564                 {
00565                     if ( $i !== $valueKeys[$i] )
00566                     {
00567                         $isIndexed = false;
00568                         break;
00569                     }
00570                 }
00571                 $i = 0;
00572                 foreach ( $valueKeys as $key )
00573                 {
00574                     if ( $i > 0 )
00575                     {
00576                         $text .= ",\n" . str_repeat( ' ', $column );
00577                     }
00578                     $element = $value[$key];
00579                     $keyText = ' ';
00580                     if ( !$isIndexed )
00581                     {
00582                         if ( is_int( $key ) )
00583                             $keyText = $key;
00584                         else
00585                             $keyText = "\"" . str_replace( array( "\\",
00586                                                                   "\"",
00587                                                                   "\n" ),
00588                                                            array( "\\\\",
00589                                                                   "\\\"",
00590                                                                   "\\n" ),
00591                                                            $key ) . "\"";
00592                         $keyText = " $keyText => ";
00593                     }
00594 
00595                     $text .= $keyText . $this->thisVariableText( $element, $column + strlen( $keyText  ), $iteration + 1, $maxIterations );
00596 
00597                     ++$i;
00598                 }
00599                 if ( $i > 0 )
00600                     $text .= ' ';
00601                 $text .= ')';
00602             }
00603         }
00604         else
00605             $text = 'null';
00606         return $text;
00607     }
00608 
00609     static function variableText( $value, $column = 0, $iteration = 0, $maxIterations = false )
00610     {
00611         // the last parameter will always be ignored
00612         $maxIterations = false;
00613 
00614         if ( $value === true  )
00615             $text = 'true';
00616         else if ( $value === false )
00617             $text = 'false';
00618         else if ( $value === null )
00619             $text = 'null';
00620         else if ( is_string( $value ) )
00621         {
00622             $valueText = str_replace( array( "\\",
00623                                              "\"",
00624                                              "\$",
00625                                              "\n" ),
00626                                       array( "\\\\",
00627                                              "\\\"",
00628                                              "\\$",
00629                                              "\\n" ),
00630                                       $value );
00631             $text = "\"$valueText\"";
00632         }
00633         else if ( is_numeric( $value ) )
00634             $text = $value;
00635         else if ( is_object( $value ) )
00636         {
00637             $text = '';
00638             if ( method_exists( $value, 'serializeData' ) )
00639             {
00640                 $serializeData = $value->serializeData();
00641                 $className = $serializeData['class_name'];
00642                 $text = "new $className(";
00643 
00644                 $column += strlen( $text );
00645                 $parameters = $serializeData['parameters'];
00646                 $variables = $serializeData['variables'];
00647 
00648                 $i = 0;
00649                 foreach ( $parameters as $parameter )
00650                 {
00651                     if ( $i > 0 )
00652                     {
00653                         $text .= ",\n" . str_repeat( ' ', $column );
00654                     }
00655                     $variableName = $variables[$parameter];
00656                     $variableValue = $value->$variableName;
00657                     $keyText = " ";
00658 
00659                     $text .= $keyText . eZPHPCreator::variableText( $variableValue, $column + strlen( $keyText  ), $iteration + 1 );
00660                     ++$i;
00661                 }
00662                 if ( $i > 0 )
00663                     $text .= ' ';
00664 
00665                 $text .= ')';
00666             }
00667         }
00668         else if ( is_array( $value ) )
00669         {
00670             $text = 'array(';
00671             $column += strlen( $text );
00672             $valueKeys = array_keys( $value );
00673             $isIndexed = true;
00674             for ( $i = 0; $i < count( $valueKeys ); ++$i )
00675             {
00676                 if ( $i !== $valueKeys[$i] )
00677                 {
00678                     $isIndexed = false;
00679                     break;
00680                 }
00681             }
00682             $i = 0;
00683             foreach ( $valueKeys as $key )
00684             {
00685                 if ( $i > 0 )
00686                 {
00687                     $text .= ",\n" . str_repeat( ' ', $column );
00688                 }
00689                 $element = $value[$key];
00690                 $keyText = ' ';
00691                 if ( !$isIndexed )
00692                 {
00693                     if ( is_int( $key ) )
00694                         $keyText = $key;
00695                     else
00696                         $keyText = "\"" . str_replace( array( "\\",
00697                                                               "\"",
00698                                                               "\n" ),
00699                                                        array( "\\\\",
00700                                                               "\\\"",
00701                                                               "\\n" ),
00702                                                        $key ) . "\"";
00703                     $keyText = " $keyText => ";
00704                 }
00705 
00706                 $text .= $keyText . eZPHPCreator::variableText( $element, $column + strlen( $keyText  ), $iteration + 1 );
00707 
00708                 ++$i;
00709             }
00710             if ( $i > 0 )
00711                 $text .= ' ';
00712             $text .= ')';
00713         }
00714         else
00715             $text = 'null';
00716         return $text;
00717     }
00718 
00719     /*!
00720      \static
00721      Splits \a $text into multiple lines using \a $splitString for splitting.
00722      For each line it will prepend the string \a $spacingString n times as specified by \a $spacing.
00723 
00724      It will try to be smart and not do anything when \a $spacing is set to \c 0.
00725 
00726      \param $skipEmptyLines If \c true it will not prepend the string for empty lines.
00727      \param $spacing Must be a positive number, \c 0 means to not prepend anything.
00728     */
00729     static function prependSpacing( $text, $spacing, $skipEmptyLines = true, $spacingString = " ", $splitString = "\n" )
00730     {
00731         if ( $spacing == 0 )
00732             return $text;
00733         $textArray = explode( $splitString, $text );
00734         $newTextArray = array();
00735         foreach ( $textArray as $text )
00736         {
00737             if ( trim( $text ) != '' )
00738                 $textLine = str_repeat( $spacingString, $spacing ) . $text;
00739             else
00740                 $textLine = $text;
00741             $newTextArray[] = $textLine;
00742         }
00743         return implode( $splitString, $newTextArray );
00744     }
00745 
00746     //@{
00747 
00748     /*!
00749      Opens the file for writing and sets correct file permissions.
00750      \return The current file resource or \c false if it failed to open the file.
00751      \note The file name and path is supplied to the constructor of this class.
00752      \note Multiple calls to this method will only open the file once.
00753     */
00754     function open( $atomic = false )
00755     {
00756         if ( $this->ClusteringEnabled )
00757             return true;
00758 
00759         if ( !$this->FileResource )
00760         {
00761             if ( !file_exists( $this->PHPDir ) )
00762             {
00763                 eZDir::mkdir( $this->PHPDir, false, true );
00764             }
00765             $path = $this->PHPDir . '/' . $this->PHPFile;
00766             $oldumask = umask( 0 );
00767             $pathExisted = file_exists( $path );
00768             if ( $atomic )
00769             {
00770                 $this->isAtomic = true;
00771                 $this->requestedFilename = $path;
00772                 $uniqid = md5( uniqid( "ezp". getmypid(), true ) );
00773                 $path .= ".$uniqid";
00774                 $this->tmpFilename = $path;
00775             }
00776             $ini = eZINI::instance();
00777             $perm = octdec( $ini->variable( 'FileSettings', 'StorageFilePermissions' ) );
00778             $this->FileResource = @fopen( $this->FilePrefix . $path, "w" );
00779             if ( !$this->FileResource )
00780                 eZDebug::writeError( "Could not open file '$path' for writing, perhaps wrong permissions" );
00781             if ( $this->FileResource and
00782                  !$pathExisted )
00783                 chmod( $path, $perm );
00784             umask( $oldumask );
00785         }
00786         return $this->FileResource;
00787     }
00788 
00789     /*!
00790      Closes the currently open file if any.
00791     */
00792     function close()
00793     {
00794         if ( $this->ClusteringEnabled )
00795         {
00796             $this->ClusterHandler = null;
00797             return;
00798         }
00799 
00800         if ( $this->FileResource )
00801         {
00802             fclose( $this->FileResource );
00803 
00804             if ( $this->isAtomic )
00805             {
00806                 eZFile::rename( $this->tmpFilename, $this->requestedFilename, false, eZFile::CLEAN_ON_FAILURE );
00807             }
00808             $this->FileResource = false;
00809         }
00810     }
00811 
00812     /*!
00813      \return \c true if the file and path already exists.
00814      \note The file name and path is supplied to the constructor of this class.
00815     */
00816     function exists()
00817     {
00818         $path = $this->PHPDir . '/' . $this->PHPFile;
00819         if ( !$this->ClusteringEnabled )
00820             return file_exists( $path );
00821 
00822         if ( !$this->ClusterHandler )
00823         {
00824             $this->ClusterHandler = eZClusterFileHandler::instance();
00825         }
00826         return $this->ClusterHandler->fileExists( $path );
00827     }
00828 
00829     /*!
00830      \return \c true if file exists and can be restored.
00831      \param $timestamp The timestamp to check the modification time of the file against,
00832                        if the modification time is larger or equal to \a $timestamp
00833                        the file can be restored. Otherwise the file is considered too old.
00834      \note The file name and path is supplied to the constructor of this class.
00835     */
00836     function canRestore( $timestamp = false )
00837     {
00838         $path = $this->PHPDir . '/' . $this->PHPFile;
00839 
00840         if ( $this->ClusteringEnabled )
00841         {
00842             if ( !$this->ClusterHandler )
00843             {
00844                 $this->ClusterHandler = eZClusterFileHandler::instance( $path );
00845             }
00846             // isExpired() expects -1 to disable expiry check
00847             $expiry = ( $timestamp === false ) ? -1 : $timestamp;
00848             return !$this->ClusterHandler->isExpired( $expiry, time(), null );
00849         }
00850 
00851         $canRestore = file_exists( $path );
00852         if ( $timestamp !== false and
00853              $canRestore )
00854         {
00855             $cacheModifierTime = filemtime( $path );
00856             $canRestore = ( $cacheModifierTime >= $timestamp );
00857         }
00858 
00859         return $canRestore;
00860     }
00861 
00862     /*!
00863      Tries to restore the PHP file and fetch the defined variables in \a $variableDefinitions.
00864      This basically means including the file using include().
00865 
00866      \param $variableDefinitions Associative array with the return variable name being the key
00867                                  matched variable as value.
00868 
00869      \return An associatve array with the variables that were found according to \a $variableDefinitions.
00870 
00871      Example:
00872      \code
00873 $values = $php->restore( array( 'MyValue' => 'node' ) );
00874 print( $values['MyValue'] );
00875      \endcode
00876 
00877      \note The file name and path is supplied to the constructor of this class.
00878     */
00879     function restore( $variableDefinitions )
00880     {
00881         $returnVariables = array();
00882         $path = $this->PHPDir . '/' . $this->PHPFile;
00883 
00884         if ( !$this->ClusteringEnabled )
00885         {
00886             $returnVariables = $this->_restoreCall( $path, false, $variableDefinitions );
00887         }
00888         else
00889         {
00890             if ( !$this->ClusterHandler )
00891             {
00892                 $this->ClusterHandler = eZClusterFileHandler::instance( $path );
00893             }
00894             $returnVariables = $this->ClusterHandler->processFile( array( $this, '_restoreCall' ), null, $variableDefinitions );
00895         }
00896         return $returnVariables;
00897     }
00898 
00899     /*!
00900      \private
00901      Processes the PHP file and returns the specified data.
00902      */
00903     function _restoreCall( $path, $mtime, $variableDefinitions )
00904     {
00905         $returnVariables = array();
00906         include( $path );
00907         foreach ( $variableDefinitions as $variableReturnName => $variableName )
00908         {
00909             $variableRequired = true;
00910             $variableDefault = false;
00911             if ( is_array( $variableName ) )
00912             {
00913                 $variableDefinition = $variableName;
00914                 $variableName = $variableDefinition['name'];
00915                 $variableRequired = $variableDefinition['required'];
00916                 if ( isset( $variableDefinition['default'] ) )
00917                     $variableDefault = $variableDefinition['default'];
00918             }
00919             if ( isset( $$variableName ) )
00920             {
00921                 $returnVariables[$variableReturnName] = $$variableName;
00922             }
00923             else if ( $variableRequired )
00924             {
00925                 eZDebug::writeError( "Variable '$variableName' is not present in cache '$path'", __METHOD__ );
00926             }
00927             else
00928             {
00929                 $returnVariables[$variableReturnName] = $variableDefault;
00930             }
00931         }
00932         return $returnVariables;
00933     }
00934 
00935     /*!
00936      Stores the PHP cache, returns false if the cache file could not be created.
00937     */
00938     function store( $atomic = false )
00939     {
00940         if ( $this->open( $atomic ) )
00941         {
00942             $this->write( "<?php\n" );
00943 
00944             $this->writeElements();
00945 
00946             $this->write( "?>\n" );
00947 
00948             $this->writeChunks();
00949             $this->flushChunks();
00950             $this->close();
00951 
00952             if ( !$this->ClusteringEnabled )
00953             {
00954                 $perm = octdec( eZINI::instance()->variable( 'FileSettings', 'StorageFilePermissions' ) );
00955                 chmod( eZDir::path( array( $this->PHPDir, $this->PHPFile ) ), $perm );
00956             }
00957 
00958             // Write log message to storage.log
00959             eZLog::writeStorageLog( $this->PHPFile, $this->PHPDir . '/' );
00960             return true;
00961         }
00962         else
00963         {
00964             eZDebug::writeError( "Failed to open file '" . $this->PHPDir . '/' . $this->PHPFile . "'", __METHOD__ );
00965             return false;
00966         }
00967     }
00968 
00969     /*!
00970      Creates a text string out of all elements and returns it.
00971      \note Calling this multiple times will resulting text processing each time.
00972     */
00973     function fetch( $addPHPMarkers = true )
00974     {
00975         if ( $addPHPMarkers )
00976             $this->write( "<?php\n" );
00977         $this->writeElements();
00978         if ( $addPHPMarkers )
00979             $this->write( "?>\n" );
00980 
00981         $text = implode( '', $this->TextChunks );
00982 
00983         $this->flushChunks();
00984 
00985         return $text;
00986     }
00987 
00988     //@}
00989 
00990     /*!
00991      \private
00992     */
00993     function writeChunks()
00994     {
00995         $count = count( $this->TextChunks );
00996 
00997         if ( $this->ClusteringEnabled )
00998         {
00999             $text = '';
01000             for ( $i = 0; $i < $count; ++$i )
01001                 $text .= $this->TextChunks[$i];
01002 
01003             $filePath = $this->FilePrefix . $this->PHPDir . '/' . $this->PHPFile;
01004 
01005             if ( !$this->ClusterHandler )
01006             {
01007                 $this->ClusterHandler = eZClusterFileHandler::instance();
01008             }
01009             $this->ClusterHandler->fileStoreContents( $filePath, $text, $this->ClusterFileScope, 'php' );
01010 
01011             return;
01012         }
01013 
01014         for ( $i = 0; $i < $count; ++$i )
01015         {
01016             $text = $this->TextChunks[$i];
01017             fwrite( $this->FileResource, $text );
01018         }
01019     }
01020 
01021     /*!
01022      \private
01023     */
01024     function flushChunks()
01025     {
01026         $this->TextChunks = array();
01027     }
01028 
01029     /*!
01030      \private
01031     */
01032     function write( $text )
01033     {
01034         $this->TextChunks[] = $text;
01035     }
01036 
01037     /*!
01038      \private
01039     */
01040     function writeElements()
01041     {
01042         foreach( $this->Elements as $element )
01043         {
01044             if ( $element[0] == eZPHPCreator::DEFINE )
01045             {
01046                 $this->writeDefine( $element );
01047             }
01048             else if ( $element[0] == eZPHPCreator::RAW_VARIABLE )
01049             {
01050                 $this->writeRawVariable( $element[1], $element[2] );
01051             }
01052             else if ( $element[0] == eZPHPCreator::VARIABLE )
01053             {
01054                 $this->writeVariable( $element[1], $element[2], $element[3], $element[4] );
01055             }
01056             else if ( $element[0] == eZPHPCreator::VARIABLE_UNSET )
01057             {
01058                 $this->writeVariableUnset( $element );
01059             }
01060             else if ( $element[0] == eZPHPCreator::VARIABLE_UNSET_LIST )
01061             {
01062                 $this->writeVariableUnsetList( $element );
01063             }
01064             else if ( $element[0] == eZPHPCreator::SPACE )
01065             {
01066                 $this->writeSpace( $element );
01067             }
01068             else if ( $element[0] == eZPHPCreator::TEXT )
01069             {
01070                 $this->writeText( $element );
01071             }
01072             else if ( $element[0] == eZPHPCreator::METHOD_CALL )
01073             {
01074                 $this->writeMethodCall( $element );
01075             }
01076             else if ( $element[0] == eZPHPCreator::CODE_PIECE )
01077             {
01078                 $this->writeCodePiece( $element );
01079             }
01080             else if ( $element[0] == eZPHPCreator::EOL_COMMENT )
01081             {
01082                 $this->writeComment( $element );
01083             }
01084             else if ( $element[0] == eZPHPCreator::INCLUDE_STATEMENT )
01085             {
01086                 $this->writeInclude( $element );
01087             }
01088         }
01089     }
01090 
01091     /*!
01092      \private
01093     */
01094     function writeDefine( $element )
01095     {
01096         $name = $element[1];
01097         $value = $element[2];
01098         $caseSensitive = $element[3];
01099         $parameters = $element[4];
01100         $text = '';
01101         if ( $this->Spacing )
01102         {
01103             $spacing = 0;
01104             if ( isset( $parameters['spacing'] ) )
01105                 $spacing = $parameters['spacing'];
01106             $text = str_repeat( ' ', $spacing );
01107         }
01108         $nameText = $this->thisVariableText( $name, 0 );
01109         $valueText = $this->thisVariableText( $value, 0 );
01110         $text .= "define( $nameText, $valueText";
01111         if ( !$caseSensitive )
01112             $text .= ", true";
01113         $text .= " );\n";
01114         $this->write( $text );
01115     }
01116 
01117     /*!
01118      \private
01119     */
01120     function writeInclude( $element )
01121     {
01122         $includeFile = $element[1];
01123         $includeType = $element[2];
01124         $parameters = $element[3];
01125         if ( $includeType == eZPHPCreator::INCLUDE_ONCE_STATEMENT )
01126             $includeName = 'include_once';
01127         else if ( $includeType == eZPHPCreator::INCLUDE_ALWAYS_STATEMENT )
01128             $includeName = 'include';
01129         $includeFileText = $this->thisVariableText( $includeFile, 0 );
01130         $text = "$includeName( $includeFileText );\n";
01131         if ( $this->Spacing )
01132         {
01133             $spacing = 0;
01134             if ( isset( $parameters['spacing'] ) )
01135                 $spacing = $parameters['spacing'];
01136             $text = str_repeat( ' ', $spacing ) . $text;
01137         }
01138         $this->write( $text );
01139     }
01140 
01141     /*!
01142      \private
01143     */
01144     function writeComment( $element )
01145     {
01146         $elementAttributes = $element[2];
01147         $spacing = 0;
01148         if ( isset( $elementAttributes['spacing'] ) and $this->Spacing )
01149             $spacing = $elementAttributes['spacing'];
01150         $whitespaceHandling = $elementAttributes['whitespace-handling'];
01151         $eol = $elementAttributes['eol'];
01152         $newCommentArray = array();
01153         $commentArray = preg_split( "/\r\n|\r|\n/", $element[1] );
01154         foreach ( $commentArray as $comment )
01155         {
01156             $textLine = '// ' . $comment;
01157             if ( $whitespaceHandling )
01158             {
01159                 $textLine = rtrim( $textLine );
01160                 $textLine = str_replace( "\t", '    ', $textLine );
01161             }
01162             $textLine = str_repeat( ' ', $spacing ) . $textLine;
01163             $newCommentArray[] = $textLine;
01164         }
01165         $text = implode( "\n", $newCommentArray );
01166         if ( $eol )
01167             $text .= "\n";
01168         $this->write( $text );
01169     }
01170 
01171     /*!
01172      \private
01173     */
01174     function writeSpace( $element )
01175     {
01176         $text = str_repeat( "\n", $element[1] );
01177         $this->write( $text );
01178     }
01179 
01180     /*!
01181      \private
01182     */
01183     function writeCodePiece( $element )
01184     {
01185         $code = $element[1];
01186         $parameters = $element[2];
01187         $spacing = 0;
01188         if ( isset( $parameters['spacing'] ) and $this->Spacing )
01189             $spacing = $parameters['spacing'];
01190         $text = eZPHPCreator::prependSpacing( $code, $spacing );
01191         $this->write( $text );
01192     }
01193 
01194     /*!
01195      \private
01196     */
01197     function writeText( $element )
01198     {
01199         $text = $element[1];
01200         $this->write( "\n?>" );
01201         $this->write( $text );
01202         $this->write( "<?php\n" );
01203     }
01204 
01205     /*!
01206      \private
01207     */
01208     function writeMethodCall( $element )
01209     {
01210         $objectName = $element[1];
01211         $methodName = $element[2];
01212         $methodParameters = $element[3];
01213         $returnValue = $element[4];
01214         $parameters = $element[5];
01215         $text = '';
01216         $spacing = 0;
01217         if ( isset( $parameters['spacing'] ) and $this->Spacing )
01218             $spacing = $parameters['spacing'];
01219         if ( is_array( $returnValue ) )
01220         {
01221             $variableName = $returnValue[0];
01222             $assignmentType = eZPHPCreator::VARIABLE_ASSIGNMENT;
01223             if ( isset( $variableValue[1] ) )
01224                 $assignmentType = $variableValue[1];
01225             $text = $this->variableNameText( $variableName, $assignmentType );
01226         }
01227         $text .= '$' . $objectName . '->' . $methodName . '(';
01228         $column = strlen( $text );
01229         $i = 0;
01230         foreach ( $methodParameters as $parameterData )
01231         {
01232             if ( $i > 0 )
01233                 $text .= ",\n" . str_repeat( ' ', $column );
01234             $parameterType = eZPHPCreator::METHOD_CALL_PARAMETER_VALUE;
01235             $parameterValue = $parameterData[0];
01236             if ( isset( $parameterData[1] ) )
01237                 $parameterType = $parameterData[1];
01238             if ( $parameterType == eZPHPCreator::METHOD_CALL_PARAMETER_VALUE )
01239                  $text .= ' ' . $this->thisVariableText( $parameterValue, $column + 1 );
01240             else if ( $parameterType == eZPHPCreator::METHOD_CALL_PARAMETER_VARIABLE )
01241                 $text .= ' $' . $parameterValue;
01242             ++$i;
01243         }
01244         if ( $i > 0 )
01245             $text .= ' ';
01246         $text .= ");\n";
01247         $text = eZPHPCreator::prependSpacing( $text, $spacing );
01248         $this->write( $text );
01249     }
01250 
01251     /*!
01252      \private
01253     */
01254     function writeVariableUnset( $element )
01255     {
01256         $variableName = $element[1];
01257         $parameters = $element[2];
01258         $spacing = 0;
01259         if ( isset( $parameters['spacing'] ) and $this->Spacing )
01260             $spacing = $parameters['spacing'];
01261         $text = "unset( \$$variableName );\n";
01262         $text = eZPHPCreator::prependSpacing( $text, $spacing );
01263         $this->write( $text );
01264     }
01265 
01266     /*!
01267      \private
01268     */
01269     function writeVariableUnsetList( $element )
01270     {
01271         $variableNames = $element[1];
01272 
01273         if ( count( $variableNames ) )
01274         {
01275             $parameters = $element[2];
01276             $spacing = 0;
01277             if ( isset( $parameters['spacing'] ) and $this->Spacing )
01278                 $spacing = $parameters['spacing'];
01279             $text = 'unset( ';
01280             array_walk( $variableNames, create_function( '&$variableName,$key', '$variableName = "\$" . $variableName;') );
01281             $text .= join( ', ', $variableNames );
01282             $text .= " );\n";
01283             $text = eZPHPCreator::prependSpacing( $text, $spacing );
01284             $this->write( $text );
01285         }
01286     }
01287 
01288     /*!
01289      \private
01290     */
01291     function writeRawVariable( $variableName, $variableValue )
01292     {
01293         $this->write( "\${$variableName} = ". var_export( $variableValue, true). ";\n" );
01294     }
01295 
01296     /*!
01297      \private
01298     */
01299     function writeVariable( $variableName, $variableValue, $assignmentType = eZPHPCreator::VARIABLE_ASSIGNMENT,
01300                             $variableParameters = array() )
01301     {
01302         $variableParameters = array_merge( array( 'full-tree' => false,
01303                                                   'spacing' => 0 ),
01304                                            $variableParameters );
01305         $fullTree = $variableParameters['full-tree'];
01306         $spacing = $this->Spacing ? $variableParameters['spacing'] : 0;
01307         $text = $this->variableNameText( $variableName, $assignmentType, $variableParameters );
01308         $maxIterations = 2;
01309         if ( $fullTree )
01310             $maxIterations = false;
01311         $text .= $this->thisVariableText( $variableValue, strlen( $text ), 0, $maxIterations );
01312         $text .= ";\n";
01313         $text = eZPHPCreator::prependSpacing( $text, $spacing );
01314         $this->write( $text );
01315     }
01316 
01317     /*!
01318      \private
01319     */
01320     function temporaryVariableName( $prefix )
01321     {
01322         $variableName = $prefix . '_' . $this->TemporaryCounter;
01323         ++$this->TemporaryCounter;
01324         return $variableName;
01325     }
01326 
01327     /// \privatesection
01328     public $PHPDir;
01329     public $PHPFile;
01330     public $FileResource;
01331     public $Elements;
01332     public $TextChunks;
01333     public $isAtomic;
01334     public $tmpFilename;
01335     public $requestedFilename;
01336     public $Spacing = true;
01337     public $ClusteringEnabled = false;
01338     public $ClusterFileScope  = false;
01339 }
01340 ?>