I am a PHP newbie, gone through a couple starter tutorials which went ok, so I thought I'd try creating a more complex project to get some experience. It is a NetBeans PHP project where I try to replicate some of the functionality from Microsoft's CodeDOM technology and Zend_CodeGenerator. Basically it is a class library that helps generating PHP code. My goals with this are:
- Get accustomed to NetBeans
- Get some routine in writing PHP code
- Learn OOP
- Learn about unit tests
- Learn about versioning
I uploaded the project to GitHub (here). I would be thankful for any comments.
The base class:
<?php
/**
* @package UC_PHPCodeModel
* @author Uros Calakovic
*/
/**
* The CodeObject class is the base class for most code model objects.
*
* @abstract
*/
abstract class CodeObject
{
public function __construct()
{
$this->Comments = new CommentStatementCollection();
}
/**
* __set() is used in CodeObject and inherited classes
* to make private / protected variables accessible from outside the class.
*
* __set() simply calls the appropriate field setter method.
* It is still possible to call field setter methods directly.
*
* @param string $name Name of the field to be accessed
* @param mixed $value The value the field should be set to, usually a collection.
*/
public function __set($name, $value)
{
if($name == 'Comments')
{
$this->set_Comments($value);
}
}
/**
* __get() is used in CodeObject and inherited classes
* to make private / protected variables accessible from outside the class.
*
* __get calls the appropriate field getter method.
* It is still possible to call field getter method directly.
*
* @param string $name Name of the field to be accessed
* @return mixed The field that $name specified, usually a collection
*/
public function __get($name)
{
if($name == 'Comments')
{
return $this->get_Comments();
}
}
/**
* The $UserData getter method
*
* @return mixed Returns any value stored in $UserData
*/
public function get_UserData()
{
return $this->UserData;
}
/**
* The $UserData setter method
*
* @param mixed $UserData An arbitrary value
*/
public function set_UserData($UserData)
{
$this->UserData = $UserData;
}
/**
* The $Comments getter method
*
* @return CommentStatementCollection
*/
public function get_Comments()
{
return $this->Comments;
}
/**
* The $Comments setter method
*
* @param CommentStatementCollection $Comments
*/
public function set_Comments(CommentStatementCollection $Comments)
{
$this->Comments = $Comments;
}
/**
* Stores the collection of comments for a code object.
*
* @var CommentStatementCollection
*/
protected $Comments;
/**
* Can contain arbitrary user-defined data related to a code object.
*
* @var mixed
*/
protected $UserData = null;
}
?>
The child class representing a PHP statement:
<?php
include_once 'CodeObject.php';
/**
* This is a parent class for other statement classes
* and should not be instantiated.
*
* @package UC_PHPCodeModel
* @author Uros Calakovic
*/
class Statement extends CodeObject
{
public function __construct()
{
parent::__construct();
}
public function __set($name, $value)
{
parent::__set($name, $value);
if($name == 'BlankLinesAfter')
{
$this->set_BlankLinesAfter($value);
}
}
public function __get($name)
{
$ret = parent::__get($name);
if($ret != null)
{
return $ret;
}
if($name == 'BlankLinesAfter')
{
return $this->get_BlankLinesAfter();
}
}
public function get_BlankLinesAfter()
{
return $this->BlankLinesAfter;
}
public function set_BlankLinesAfter($BlankLinesAfter)
{
if(is_int($BlankLinesAfter) )
{
if($BlankLinesAfter >= 0)
{
$this->BlankLinesAfter = $BlankLinesAfter;
}
else
{
throw new InvalidArgumentException(
'The value should be greater or equal to zero.');
}
}
else
{
throw new InvalidArgumentException(
'The value should be an integer value.');
}
}
private $BlankLinesAfter = 0;
}
?>
The child class representing an if..else statement:
<?php
include_once 'ConditionStatement.php';
include_once 'StatementCollection.php';
/**
* Represents an if...else statement
*
* elseif is not supported
*
* @package UC_PHPCodeModel
* @author Uros Calakovic
* @example ../Classes/IfElseStatementExample.php
*/
class IfElseStatement extends ConditionStatement
{
/**
* The IfElseStatement constructor
*
* Both $TrueStatements and $FalseStatements can be ommited by setting them to null
*
* @param Expression $Condition The boolean expression to be evalueted true or false
* @param StatementCollection $TrueStatements The statements in the if block
* @param StatementCollection $FalseStatements The statements in the else block
*/
public function __construct(Expression $Condition,
StatementCollection $TrueStatements = null,
StatementCollection $FalseStatements = null)
{
parent::__construct();
$this->set_Condition($Condition);
$TrueStatements == null ?
$this->TrueStatements = new StatementCollection() :
$this->set_TrueStatements($TrueStatements);
$FalseStatements == null ?
$this->FalseStatements = new StatementCollection() :
$this->set_FalseStatements($FalseStatements);
}
/**
* Used for making private / protected variables
* accessible from outside the class.
*
* @param string $name Name of the field to be accessed
* @param mixed $value The value the field should be set to, usually a collection.
* @see CodeObject::__set()
*/
public function __set($name, $value)
{
parent::__set($name, $value);
if($name == 'TrueStatements')
{
$this->set_TrueStatements($value);
}
if($name == 'FalseStatements')
{
$this->set_FalseStatements($value);
}
}
/**
* Used for making private / protected variables
* accessible from outside the class.
*
* @param string $name Name of the field to be accessed
* @return mixed The field that $name specified, usually a collection
* @see CodeObject::__get()
*/
public function __get($name)
{
$ret = parent::__get($name);
if($ret != null)
{
return $ret;
}
if($name == 'TrueStatements')
{
return $this->get_TrueStatements();
}
if($name == 'FalseStatements')
{
return $this->get_FalseStatements();
}
}
/**
* The $TrueStatements getter method
* @return StatementCollection
*/
public function get_TrueStatements()
{
return $this->TrueStatements;
}
/**
* The $TrueStatements setter method
* @param StatementCollection $TrueStatements
*
* @assert (123) throws PHPUnit_Framework_Error
* @assert ('name') throws PHPUnit_Framework_Error
* @assert (new stdClass()) throws PHPUnit_Framework_Error
*/
public function set_TrueStatements(StatementCollection $TrueStatements)
{
$this->TrueStatements = $TrueStatements;
}
/**
* The $FalseStatements getter method
* @return StatementCollection
*/
public function get_FalseStatements()
{
return $this->FalseStatements;
}
/**
* The $FalseStatements setter method
* @param StatementCollection $FalseStatements
*
* @assert (123) throws PHPUnit_Framework_Error
* @assert ('name') throws PHPUnit_Framework_Error
* @assert (new stdClass()) throws PHPUnit_Framework_Error
*/
public function set_FalseStatements(StatementCollection $FalseStatements)
{
$this->FalseStatements = $FalseStatements;
}
/**
* The statements in the if block
* @var StatementCollection
*/
private $TrueStatements;
/**
* The statements in the else block
* @var StatementCollection
*/
private $FalseStatements;
}
?>
The test unit class for the if..else class:
<?php
require_once dirname(__FILE__) . '/../../Classes/IfElseStatement.php';
/**
* Test class for IfElseStatement.
* Generated by PHPUnit on 2012-05-23 at 21:05:00.
*/
class IfElseStatementTest extends PHPUnit_Framework_TestCase
{
/**
* @var IfElseStatement
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->object = new IfElseStatement(
new BinaryOperatorExpression(
BinaryOperator::VALUE_EQUALITY,
new VariableReferenceExpression('a'),
new PrimitiveExpression(0)));
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* Generated from @assert (123) throws PHPUnit_Framework_Error.
*
* @covers IfElseStatement::set_TrueStatements
* @expectedException PHPUnit_Framework_Error
*/
public function testSet_TrueStatements()
{
$this->object->set_TrueStatements(123);
}
/**
* Generated from @assert ('name') throws PHPUnit_Framework_Error.
*
* @covers IfElseStatement::set_TrueStatements
* @expectedException PHPUnit_Framework_Error
*/
public function testSet_TrueStatements2()
{
$this->object->set_TrueStatements('name');
}
/**
* Generated from @assert (new stdClass()) throws PHPUnit_Framework_Error.
*
* @covers IfElseStatement::set_TrueStatements
* @expectedException PHPUnit_Framework_Error
*/
public function testSet_TrueStatements3()
{
$this->object->set_TrueStatements(new stdClass());
}
/**
* Generated from @assert (123) throws PHPUnit_Framework_Error.
*
* @covers IfElseStatement::set_FalseStatements
* @expectedException PHPUnit_Framework_Error
*/
public function testSet_FalseStatements()
{
$this->object->set_FalseStatements(123);
}
/**
* Generated from @assert ('name') throws PHPUnit_Framework_Error.
*
* @covers IfElseStatement::set_FalseStatements
* @expectedException PHPUnit_Framework_Error
*/
public function testSet_FalseStatements2()
{
$this->object->set_FalseStatements('name');
}
/**
* Generated from @assert (new stdClass()) throws PHPUnit_Framework_Error.
*
* @covers IfElseStatement::set_FalseStatements
* @expectedException PHPUnit_Framework_Error
*/
public function testSet_FalseStatements3()
{
$this->object->set_FalseStatements(new stdClass());
}
/**
* @covers IfElseStatement::__set
*/
public function test__set()
{
$trueStatements = new StatementCollection();
$trueStatements->add(new AssignStatement(
new VariableReferenceExpression('a'),
new PrimitiveExpression(0)));
$this->object->TrueStatements = $trueStatements;
$this->assertTrue(
$trueStatements === $this->object->get_TrueStatements());
}
/**
* @covers IfElseStatement::__set
*/
public function test__set2()
{
$falseStatements = new StatementCollection();
$falseStatements->add(new AssignStatement(
new VariableReferenceExpression('a'),
new PrimitiveExpression(0)));
$this->object->FalseStatements = $falseStatements;
$this->assertTrue(
$falseStatements === $this->object->get_FalseStatements());
}
/**
* @covers IfElseStatement::__get
*/
public function test__get()
{
$this->assertTrue(
$this->object->TrueStatements === $this->object->get_TrueStatements());
}
/**
* @covers IfElseStatement::__get
*/
public function test__get2()
{
$this->assertTrue(
$this->object->FalseStatements === $this->object->get_FalseStatements());
}
/**
* @covers IfElseStatement::get_TrueStatements
*/
public function testGet_TrueStatements()
{
$this->assertTrue(
$this->object->get_TrueStatements() instanceof StatementCollection ||
$this->object->get_TrueStatements() == null);
}
/**
* @covers IfElseStatement::get_FalseStatements
*/
public function testGet_FalseStatements()
{
$this->assertTrue(
$this->object->get_FalseStatements() instanceof StatementCollection ||
$this->object->get_FalseStatements() == null);
}
}
?>
The if..else class example usage:
<?php
include_once 'Classes/PHPCodeProvider.php';
/*
* Create an instance of the CodeGeneratorOptions class
* using the default options for code generation.
*/
$options = new CodeGeneratorOptions();
/*
* Create an instance of the IndentedTextWriter.
* After the generateCodeFromStatement call
* $writer will hold the generated code.
*/
$writer = new IndentedTextWriter();
/*
* Create an instance of the PHPCodeProvider class
*/
$provider = new PHPCodeProvider();
/*
* Variable and field references that will be used
* to generate if statements
*/
$thisReference = new ThisReferenceExpression();
$isWinning = new VariableReferenceExpression('isWinning');
$attemptNumber = new FieldReferenceExpression('attemptNumber', $thisReference);
$gameFinished = new FieldReferenceExpression('gameFinished', $thisReference);
/*
* A simple if statement
*/
$ifElseStatement1 = new IfElseStatement($isWinning);
$ifElseStatement1->TrueStatements->add(
new AssignStatement(
new FieldReferenceExpression('isWinning', $thisReference),
$isWinning));
/*
* Generate the code and output the result.
*/
$provider->generateCodeFromStatement($ifElseStatement1, $writer, $options);
echo
'<pre>' . str_replace(
'<br />', '', highlight_string($writer->get_String(), true)) .
'<pre/>';
$writer->clear();
/*
* An if statement with both the if and the else block
*/
$ifElseStatement2 = new IfElseStatement(
new BinaryOperatorExpression(
BinaryOperator::LESS_THAN,
$attemptNumber,
new PrimitiveExpression(8)));
$ifElseStatement2->TrueStatements->add(
new AssignStatement(
$attemptNumber,
new PrimitiveExpression(1),
CompoundAssignmentOperator::PLUS_ASSIGN));
$ifElseStatement2->FalseStatements->add(
new AssignStatement(
$gameFinished,
new PrimitiveExpression(true)));
/*
* Generate the code and output the result.
*/
$provider->generateCodeFromStatement($ifElseStatement2, $writer, $options);
echo
'<pre>' . str_replace(
'<br />', '', highlight_string($writer->get_String(), true)) .
'<pre/>';
/*
* The output should be:
*
* if($isWinning)
* {
* $this->isWinning = $isWinning;
* }
*
*
* if($this->attemptNumber < 8)
* {
* $this->attemptNumber += 1;
* }
* else
* {
* $this->gameFinished = true;
* }
*
*/
?>