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