blob: c5254417e00ccd757f445d482b0787a520119563 [file] [log] [blame]
<?php
/**
* File containing the ezcTemplateAstBuilder class
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Template
* @version //autogen//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @access private
*/
/**
* Easy building of PHP code elements.
*
* This class has several methods which will make it much easier to build
* PHP code elements without having to instantiate the specific code classes
* manually. The code will be much sparser and should be easier to read.
*
* The simplest usage is to instantiate this class and starting calling the
* various methods which appends a new statement to the current body.
* e.g.
* <code>
* $cb = new ezcTemplateAstBuilder;
* $cb->assign( "a", 5 ); // $a = 5
* $cb->call( "echo", $cb->variable( "a" ) ); // echo $a;
* </code>
*
* It can also create more complex code which involes control structures.
* The structures usually has a condition or expression as a parameter
* and then a body element, this is where the body() method comes into
* play. This creates a new build object which is returned, the control
* structure methods will recognize it and use the main body of it as
* CS body. However once the builder is returned from body() it can be
* used to fill in the body before it is passed to the CS.
* .e.g to create an 'if' with a body:
* <code>
* $cb->_if( true,
* $cb->body()
* ->assign( "b", 5 ) );
* </code>
* As you see almost all methods in this class returns $this, which means
* consecutive calls on the same object is possible without having to resort
* to temporary variables.
*
* Another important part is that most methods accepts PHP basic types as
* parameters, the method will convert it internally to the correct object
* and use that instead. This is useful when you only need to work with
* constant values and not complex structures. See each respective methods
* for what is allowed as parameter values.
*
* @package Template
* @version //autogen//
* @access private
*/
class ezcTemplateAstBuilder
{
/**
* The body element which starts the statements.
* @var ezcTemplateBodyAstNode
*/
private $mainBody;
/**
* The current body element.
* @var ezcTemplateBodyAstNode
* @todo This might not be needed anymore due to the body() functionality.
*/
private $currentBody;
/**
* The current element.
* @var ezcTemplateAstNode
* @todo This might not be needed anymore due to the body() functionality.
*/
private $currentElement;
/**
* Initialize with a new body elemnt.
*/
public function __construct()
{
$this->mainBody = new ezcTemplateBodyAstNode();
$this->currentBody = $this->mainBody;
$this->currentElement = $this->mainBody;
}
/**
* Returns the body element which represents the start of the code.
*
* @return ezcTemplateBodyAstNode
*/
public function getAstNode()
{
return $this->mainBody;
}
/**
* Appends a function call to the current body and returns $this.
* The $parameters can consist of PHP types in which case they are converted
* into ezcTemplateLiteralAstNode objects, in addition a single parameter can be
* passed in $parameters.
*
* @param string $name The name of the function to call.
* @param mixed $parameters The parameters for the function call.
* @return ezcTemplateAstBuilder
*/
public function call( $name, $parameters )
{
if ( !is_array( $parameters ) )
{
$parameters = array( $parameters );
}
foreach ( $parameters as $i => $parameter )
{
if ( !is_object( $parameter ) ||
!$parameter instanceof ezcTemplateAstNode )
{
$parameter = new ezcTemplateLiteralAstNode( $parameter );
$parameters[$i] = $parameter;
}
}
switch ( $name )
{
case 'echo':
$call = new ezcTemplateEchoAstNode( $parameters );
break;
default:
$call = new ezcTemplateGenericStatementAstNode( new ezcTemplateFunctionCallAstNode( $name, $parameters ) );
}
$this->currentBody->appendStatement( $call );
return $this;
}
/**
* Appends an assignment operator to the current body and returns $this.
* The left hand side can either be a code element or a string which
* is turned into an ezcTemplateVariableAstNode object.
* The right hand side can either be a code element or a PHP type which
* is turned into an ezcTemplateLiteralAstNode object.
*
* @param mixed $lhs The left hand side of the expression.
* @param mixed $rhs The right hand side of the expression.
* @return ezcTemplateAstBuilder
*/
public function assign( $lhs, $rhs )
{
if ( !is_object( $lhs ) ||
!$lhs instanceof ezcTemplateAstNode )
{
$lhs = new ezcTemplateVariableAstNode( $lhs );
}
if ( !is_object( $rhs ) ||
!$rhs instanceof ezcTemplateAstNode )
{
$rhs = new ezcTemplateLiteralAstNode( $rhs );
}
$assignment = new ezcTemplateAssignmentOperatorAstNode();
$assignment->appendParameter( $lhs );
$assignment->appendParameter( $rhs );
$this->currentBody->appendStatement( new ezcTemplateGenericStatementAstNode( $assignment ) );
return $this;
}
/**
* Appends an if control structure to the current body and returns $this.
* The $expression parameter can either be a code element or a PHP type
* which is turned into an ezcTemplateLiteralAstNode object.
* The $body parameter can either be an ezcTemplateBodyAstNode element or
* an ezcTemplateAstBuilder object in which case the body is taken from
* the builder.
*
* Usually you will call body() and use the result as the $body parameter,
* this allows you to specify the statements for the body of the if.
* <code>
* $cb->_if( true,
* $cb->body()
* ->assign( "a", 5 ) );
* </code>
* This represents the code:
* <code>
* if ( true )
* {
* $a = 5;
* }
* </code>
*
* @param mixed $expression The evaluated expression for the 'if'.
* @param mixed $body The body of the 'if' structure.
* @return ezcTemplateAstBuilder
*/
public function _if( $expression, $body )
{
if ( !is_object( $expression ) ||
!$expression instanceof ezcTemplateAstNode )
{
$expression = new ezcTemplateLiteralAstNode( $expression );
}
if ( $body instanceof ezcTemplateAstBuilder )
{
$body = $body->getAstNode();
}
$conditionBody = new ezcTemplateConditionBodyAstNode( $expression, $body );
$if = new ezcTemplateIfAstNode( $conditionBody );
$this->currentBody->appendStatement( $if );
return $this;
}
/**
* Appends an elseif control structure to the current body and returns $this.
*
* Note: This can only called directly after a call to {@link self::_if() if ()}.
*
* The $expression parameter can either be a code element or a PHP type
* which is turned into an ezcTemplateLiteralAstNode object.
* The $body parameter can either be an ezcTemplateBodyAstNode element or
* an ezcTemplateAstBuilder object in which case the body is taken from
* the builder.
*
* Usually you will call body() and use the result as the $body parameter,
* this allows you to specify the statements for the body of the else.
* <code>
* $cb->_elseif( true,
* $cb->body()
* ->assign( "a", 5 ) );
* </code>
* This represents the code:
* <code>
* elseif ( true )
* {
* $a = 5;
* }
* </code>
*
* @param mixed $expression The evaluated expression for the 'elseif'.
* @param mixed $body The body of the 'elseif' structure.
* @return ezcTemplateAstBuilder
*/
public function _elseif( $expression, $body )
{
$if = $this->currentBody->getLastStatement();
if ( !$if instanceof ezcTemplateIfAstNode )
{
throw new ezcTemplateInternalException( "An 'elseif' control structure can only be appended right after an 'if', last statement was <" . get_class( $if ) . ">" );
}
$lastCondition = $if->getLastCondition();
if ( $lastCondition !== null &&
$lastCondition->condition === null )
{
throw new ezcTemplateInternalException( "An 'elseif' control structure can only be appended right after an 'if', tried placing it after an 'else' control structure." );
}
if ( !is_object( $expression ) ||
!$expression instanceof ezcTemplateAstNode )
{
$expression = new ezcTemplateLiteralAstNode( $expression );
}
if ( $body instanceof ezcTemplateAstBuilder )
{
$body = $body->getAstNode();
}
$conditionBody = new ezcTemplateConditionBodyAstNode( $expression, $body );
$if->appendCondition( $conditionBody );
return $this;
}
/**
* Appends an else control structure to the current body and returns $this.
*
* Note: This can only called directly after a call to {@link self::_if() if ()} or {@link self::_elseif() _elseif()}.
*
* The $body parameter can either be an ezcTemplateBodyAstNode element or
* an ezcTemplateAstBuilder object in which case the body is taken from
* the builder.
*
* Usually you will call body() and use the result as the $body parameter,
* this allows you to specify the statements for the body of the else.
* <code>
* $cb->_else( $cb->body()
* ->assign( "a", 5 ) );
* </code>
* This represents the code:
* <code>
* else
* {
* $a = 5;
* }
* </code>
*
* @param mixed $body The body of the 'else' structure.
* @return ezcTemplateAstBuilder
*/
public function _else( $body )
{
$if = $this->currentBody->getLastStatement();
if ( !$if instanceof ezcTemplateIfAstNode )
{
throw new ezcTemplateInternalException( "An 'else' control structure can only be appended right after an 'if' or 'elseif', last statement was <" . get_class( $if ) . ">" );
}
$lastCondition = $if->getLastCondition();
if ( $lastCondition !== null &&
$lastCondition->condition === null )
{
throw new ezcTemplateInternalException( "An 'else' control structure can only be appended right after an 'if' or 'elseif', tried placing it after an 'else' control structure." );
}
if ( $body instanceof ezcTemplateAstBuilder )
{
$body = $body->getAstNode();
}
$conditionBody = new ezcTemplateConditionBodyAstNode( null, $body );
$if->appendCondition( $conditionBody );
return $this;
}
/**
* Appends a while control structure to the current body and returns $this.
* The $expression parameter can either be a code element or a PHP type
* which is turned into an ezcTemplateLiteralAstNode object.
* The $body parameter can either be an ezcTemplateBodyAstNode element or
* an ezcTemplateAstBuilder object in which case the body is taken from
* the builder.
*
* Usually you will call body() and use the result as the $body parameter,
* this allows you to specify the statements for the body of the if.
* <code>
* $cb->_while( true,
* $cb->body()
* ->assign( "a", 5 )
* ->_break() );
* </code>
* This represents the code:
* <code>
* while ( true )
* {
* $a = 5;
* break;
* }
* </code>
*
* @param mixed $expression The evaluated expression for the 'while'.
* @param mixed $body The body of the 'while' structure.
* @return ezcTemplateAstBuilder
*/
public function _while( $expression, $body )
{
if ( !is_object( $expression ) ||
!$expression instanceof ezcTemplateAstNode )
{
$expression = new ezcTemplateLiteralAstNode( $expression );
}
if ( $body instanceof ezcTemplateAstBuilder )
{
$body = $body->getAstNode();
}
$conditionBody = new ezcTemplateConditionBodyAstNode( $expression, $body );
$while = new ezcTemplateWhileAstNode( $conditionBody );
$this->currentBody->appendStatement( $while );
return $this;
}
/**
* Appends a do/while control structure to the current body and returns $this.
* The $expression parameter can either be a code element or a PHP type
* which is turned into an ezcTemplateLiteralAstNode object.
* The $body parameter can either be an ezcTemplateBodyAstNode element or
* an ezcTemplateAstBuilder object in which case the body is taken from
* the builder.
*
* Usually you will call body() and use the result as the $body parameter,
* this allows you to specify the statements for the body of the if.
* <code>
* $cb->_doWhile( true,
* $cb->body()
* ->assign( "a", 5 )
* ->_break() );
* </code>
* This represents the code:
* <code>
* do
* {
* $a = 5;
* break;
* } while ( true );
* </code>
*
* @param mixed $expression The evaluated expression for the 'do/while'.
* @param mixed $body The body of the 'do/while' structure.
* @return ezcTemplateAstBuilder
*/
public function _doWhile( $expression, $body )
{
if ( !is_object( $expression ) ||
!$expression instanceof ezcTemplateAstNode )
{
$expression = new ezcTemplateLiteralAstNode( $expression );
}
if ( $body instanceof ezcTemplateAstBuilder )
{
$body = $body->getAstNode();
}
$conditionBody = new ezcTemplateConditionBodyAstNode( $expression, $body );
$while = new ezcTemplateDoWhileAstNode( $conditionBody );
$this->currentBody->appendStatement( $while );
return $this;
}
/**
* Creates a variable code element with the specified variable name and
* returns it.
*
* @param string $name Name of variable.
* @return ezcTemplateVariableAstNode
*/
public function variable( $name )
{
return new ezcTemplateVariableAstNode( $name );
}
/**
* Creates a new builder and body object and returns the builder.
* The new builder can be used as parameter for all functions which
* accepts a body as a parameter. This allows the builder object to
* be used in consecutive calls without storing it in a temporary variable.
* e.g.
* <code>
* $cb->body()
* ->assign( "a", 5 );
* </code>
*
* @return ezcTemplateAstBuilder
*/
public function body()
{
$builder = new ezcTemplateAstBuilder();
return $builder;
}
}
?>