I've been trying to understand how static scope works in the context of a trait. A great explanation is here: https://stackoverflow.com/a/56935557/2137316 but it doesn't quite address my concern.
I'm attempting to create a trait for unit tests possessing a property that more or less serves as global flag. The goal is for it to prevent unnecessary reruns of its behavior if a previous test in a given run has already triggered it.
trait CreateDatabase
{
protected static bool $hasRun = false; //We don't want to waste time rebuilding the database in each test
protected runDb(): void
{
if (!self::$hasRun) {
//Do stuff...
}
self::$hasRun = true;
}
}
class SomeTestClass
{
use CreateDatabase;
/**
* #test
**/
public function canRunSomeTest()
{
$this->runDb();
//Test stuff...
}
}
The related post talks about trait context and would seem to suggest that self in this context refers to the 'context' of SomeTestClass, meaning SomeOtherTestClass would have no awareness that the database has already been created. The conclusion would then seem to be that the way to achieve the effect I'm going for would be for is to replace
self::$hasRun = true;
with
CreateDatabase::$hasRun = true;
Even though that line is being executed within the trait explicitly being referenced. The purpose being, to talk to the more-global trait context rather than that of the class using it.
The problem is, Php8, via my IDE, is fussing at me over that decision:
Calling static trait member directly is deprecated. It should only be accessed on a class using the trait.
Usually when my tooling resists me, it means there's something flawed about the overall approach, but I'm not seeing it yet. Wondering if anyone has any insight.
Use trait as proxy here:
class CreateDatabase
{
protected static bool $hasRun = false;
public static function runDb(): void
{
if (!self::$hasRun) {
//Do stuff...
}
self::$hasRun = true;
}
}
trait CreateDatabaseTrait
{
public static function runDb(): void {
CreateDatabaseTrait::runDb();
}
}
class SomeTestClass
{
use CreateDatabaseTrait;
/**
* #test
**/
public function canRunSomeTest()
{
self::runDb();
//Test stuff...
}
}
Related
Here's how I write my tests:
A class with private methods and a single public method which runs all my others private methods.
class MyTest extends PHPUnit_Framework_TestCase
{
private function firstScenario() {
$this->navigation = new Navigation($this->webDriver);
$this->navigation->goToPointA();
//...
}
private function secondScenario() {
$this->navigation = new Navigation($this->webDriver);
$this->navigation->goToPointA();
//...
}
public function testRun() {
//...
$this->firstScenario();
$this->secondScenario();
//...
}
I have in other classes some generic methods, whose one named Navigation.php. In this class, I have all my methods which enable me to go to a specific point of my application.
All I want to do is, according to a condition, to close (or quit, or dispose, or whatever you want) my test, properly, without returning an error. I tried quit(), close() and dispose() but maybe I use its wrong.
You should probably add quit() to your tear down. Depending on how you are invoking driver it would be something like:
protected function tearDown() {
if ($this->driver) {
$this->driver->quit();
}
}
I have the following class in PHP with all static methods:
class Foo {
public static function a {
}
public static function b {
}
public static function c {
}
public static function d {
}
public static function e {
}
}
Is there a way to create a hook to fire before calling any of the methods in class Foo i.e. like a before hook? I need some logic, and don't want to have to add that logic to every static function like:
class Foo {
private static function init() {
// pre logic here
}
public static function a {
Foo::init();
}
public static function b {
Foo::init();
}
public static function c {
Foo::init();
}
public static function d {
Foo::init();
}
public static function e {
Foo::init();
}
}
What you want is called an Aspect-Oriented Programming. It allows to define an advice before method call, property access, class initialization, etc.
However, this technique is not used widely in PHP due to its complexity. I can suggest your an example with Go! AOP Framework.
class AutoInitializationAspect implements Aspect
{
/**
* This advice intercepts an execution of static methods
*
* We use "Before" type of advice to initialize the state
*
* #param MethodInvocation $invocation Invocation
*
* #Before("execution(public Foo::*(*))", scope="target")
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
$class = $invocation->getThis(); // will be the class name
$class::init(); // access to the private scope of class
}
}
Visit http://demo.aopphp.com/?showcase=loggable for demo (see LoggingDemo::runByName() static method intercepted)
I guess you can use __callStatic() magic method to achieve.
public static function __callStatic($name, $arguments)
{
// pre logic here
switch($name)
{
case 'a':
// do something
break;
case 'b':
// do something
break;
}
}
Basic answer: no, there is no such thing in plain PHP.
However, you can try several options:
You can call your methods like aSomeSuffix, bSomeSuffix, etc., and call them via __callStatic method computing that suffixed name on-the-fly.
Pros:
Single handler
Cons:
Your IDE won't see those methods until you explicitly write them down via phpDoc
Extra work and big pile of places to make a mistake (arguments passing by reference, missing method handling, etc.)
You can try Go library that introduces aspect-oriented programming in PHP claims to be able to intercept static calls. I've never used it (though i've heard lots of good responses about it) and don't know anything about performance drops and/or caveats using it, but it seems to be matching your case. I guess, this would still require to write an annotation for every method, but it will result in single handler.
Call initialization method in every method. This is what you're trying to avoid and not an option, i guess, just because it's violates DRY.
This question might sound obvious and might be stupid as well. But I am trying to figure out why do I need to use interfaces? I think I can handle most of the things which interfaces do using classes then what's the point of using them? It's true that I might end up in problems if I don't interfaces but I am trying to figure out what problems are caused by not using interfaces?
One use of interfaces is that they allow us to define behaviors and put restrictions on of classes which implement them.
Another use is that interface work as types and I can use interfaces for type hinting as shown below.
//Java example
public interface IPaintable{
void Paint(System.Drawing.Color color);
}
public void Paint(IPaintable item, System.Drawing.Color color){
item.Paint(color);
}
But are there any other use of interfaces in PHP?
I.e. What advantages do I get by using interfaces in the code below.
//Non interface implementation
<?php
class DBPersonProvider
{
public function getPerson($givenName, $familyName)
{
/* go to the database, get the person... */
$person = new Person();
$person->setPrefix("Mr.");
$person->setGivenName("John");
return $person;
}
}
/* I need to get person data... */
$provider = new DBPersonProvider();
$person = $provider->getPerson("John", "Doe");
echo($person->getPrefix());
echo($person->getGivenName());
?>
//Implementation with interface
<?php
interface PersonProvider
{
public function getPerson($givenName, $familyName);
}
class DBPersonProvider implements PersonProvider
{
public function getPerson($givenName, $familyName)
{
/* pretend to go to the database, get the person... */
$person = new Person();
$person->setPrefix("Mr.");
$person->setGivenName("John");
return $person;
}
}
/* I need to get person data... */
$provider = new DBPersonProvider();
$person = $provider->getPerson("John", "Doe");
echo($person->getPrefix());
echo($person->getGivenName());
?>
I write a nice library that interacts with the database. And I use MySQL. When you purchase my library, you know it's MySQL based but you roll with SQL Server. I was considerate enough to create interfaces for the Database access. And I provided an implementation for MySQL. Now you can implement your own SQL Server wrapper around my database access interface, and then use it as a __construct() argument for the classes in the library you will use to change move storage to SQL Server.
Interfaces are very useful for library / reusable code writers like me :) They are code contracts that have to be obeyed. You know that any class that implements them WILL have a set of functions exactly as the Interface declared them. And you can also statically type them in function arguments like function(MyInterface $Object) which enforces, at PHP compiler level, that $Object must be implementing MyInterface.
PS: Abstract classes are good enough for the rest of self-written code consuming developers...
UPDATE:
/**
* Database Access functionality blueprint.
*/
interface IDatabaseAccess {
public function Connect();
public function Query();
public function Fetch();
}
/**
* Database Access functionality implementation for MySQL.
*/
class MySqlDatabaseAccess implements IDatabaseAccess {
public function Query(){
// do mysql stuff
}
public function Fetch(){
// do mysql stuff
}
}
/**
* Database Access functionality implementation for SQLServer.
*/
class SqlServerDatabaseAccess implements IDatabaseAccess {
public function Query(){
// do sqlserver stuff
}
public function Fetch(){
// do sqlserver stuff
}
}
/**
* Database Access consumer that's database system agnostic.
*/
class DatabaseAccessConsumer {
protected $_Provider = null;
public function __construct(IDatabaseAccess $Provider){
$this->_Provider = $Provider;
$this->_Provider->Connect();
}
public function Query(){
return $this->_Provider->Query();
}
public function Fetch(){
return $this->_Provider->Fetch();
}
}
^ code that should speak for itself.
Interfaces actually provide less features than abstract classes (you cannot implement anything).
But they resolve the problem of multiple inheritance. Most modern language do not allow a class to derive more than one class. By using an interface, which does not implement any method, you are sure there is no ambiguity when you invoke a method from the interface (because there is no implementation).
Example (syntactically not valid):
class A {
public foo() {
echo 'I am A and I foo';
};
public
}
class B {
public foo() {
echo 'I am B and I foo';
}
}
class C extends A, B { // invalid
public bar() {
foo(); // which one? A's or B's?
}
}
Second example:
class A {
public foo() {
echo 'I am A and I foo';
};
}
interface iB {
public foo();
public bar();
}
interface iC {
public foo();
public qux();
}
class D extends A implements iB, iC {
public bar() {
foo(); // no ambiguity, this is A::foo(), even if the method is also declared in the interfaces
}
public qux() {}
}
Interfaces are just blueprints of classes - they are ways of saying "If you are going to be doing something with this type of class, it must have this and do this." It allows you to control - to an extent - what another class will have/do at a minimum for a given situation. Not every situation calls for an iterface. Interfaces are best used in situations when you need to have some control over the basic code of certain classes but you may not be the one writing them. If you know that the extended classes will have x properties and y methods, then you can do basic future class support.
I'm building a User Class for my new website, however this time I was thinking to build it little bit differently...
C++, Java and even Ruby (and probably other programming languages) are allowing the use of nested/inner classes inside the main class, which allows us to make the code more object-oriented and organized.
In PHP, I would like to do something like so:
<?php
public class User {
public $userid;
public $username;
private $password;
public class UserProfile {
// some code here
}
private class UserHistory {
// some code here
}
}
?>
Is that possible in PHP? How can I achieve it?
UPDATE
If it's impossible, will future PHP versions might support nested classes?
Intro:
Nested classes relate to other classes a little differently than outer classes. Taking Java as an example:
Non-static nested classes have access to other members of the enclosing class, even if they are declared private. Also, non-static nested classes require an instance of the parent class to be instantiated.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
There are several compelling reasons for using them:
It is a way of logically grouping classes that are only used in one place.
If a class is useful to only one other class, then it is logical to
relate and embed it in that class and keep the two together.
It increases encapsulation.
Consider two top-level classes, A and B, where B needs access to
members of A that would otherwise be declared private. By hiding class
B within class A, A's members can be declared private and B can access
them. In addition, B itself can be hidden from the outside world.
Nested classes can lead to more readable and maintainable code.
A nested class usually relates to it's parent class and together form a "package"
In PHP
You can have similar behavior in PHP without nested classes.
If all you want to achieve is structure/organization, as Package.OuterClass.InnerClass, PHP namespaces might sufice. You can even declare more than one namespace in the same file (although, due to standard autoloading features, that might not be advisable).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
If you desire to emulate other characteristics, such as member visibility, it takes a little more effort.
Defining the "package" class
namespace {
class Package {
/* protect constructor so that objects can't be instantiated from outside
* Since all classes inherit from Package class, they can instantiate eachother
* simulating protected InnerClasses
*/
protected function __construct() {}
/* This magic method is called everytime an inaccessible method is called
* (either by visibility contrains or it doesn't exist)
* Here we are simulating shared protected methods across "package" classes
* This method is inherited by all child classes of Package
*/
public function __call($method, $args) {
//class name
$class = get_class($this);
/* we check if a method exists, if not we throw an exception
* similar to the default error
*/
if (method_exists($this, $method)) {
/* The method exists so now we want to know if the
* caller is a child of our Package class. If not we throw an exception
* Note: This is a kind of a dirty way of finding out who's
* calling the method by using debug_backtrace and reflection
*/
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
if (isset($trace[2])) {
$ref = new ReflectionClass($trace[2]['class']);
if ($ref->isSubclassOf(__CLASS__)) {
return $this->$method($args);
}
}
throw new \Exception("Call to private method $class::$method()");
} else {
throw new \Exception("Call to undefined method $class::$method()");
}
}
}
}
Use case
namespace Package {
class MyParent extends \Package {
public $publicChild;
protected $protectedChild;
public function __construct() {
//instantiate public child inside parent
$this->publicChild = new \Package\MyParent\PublicChild();
//instantiate protected child inside parent
$this->protectedChild = new \Package\MyParent\ProtectedChild();
}
public function test() {
echo "Call from parent -> ";
$this->publicChild->protectedMethod();
$this->protectedChild->protectedMethod();
echo "<br>Siblings<br>";
$this->publicChild->callSibling($this->protectedChild);
}
}
}
namespace Package\MyParent
{
class PublicChild extends \Package {
//Makes the constructor public, hence callable from outside
public function __construct() {}
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
class ProtectedChild extends \Package {
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
}
Testing
$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)
Output:
Call from parent -> I'm Package protected method
I'm Package protected method
Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context
NOTE:
I really don't think trying to emulate innerClasses in PHP is such a good idea. I think the code is less clean and readable. Also, there are probably other ways to achieve similar results using a well established pattern such as the Observer, Decorator ou COmposition Pattern. Sometimes, even simple inheritance is sufficient.
Real nested classes with public/protected/private accessibility were proposed in 2013 for PHP 5.6 as an RFC but did not make it (No voting yet, no update since 2013 - as of 2021/02/03):
https://wiki.php.net/rfc/nested_classes
class foo {
public class bar {
}
}
At least, anonymous classes made it into PHP 7
https://wiki.php.net/rfc/anonymous_classes
From this RFC page:
Future Scope
The changes made by this patch mean named nested classes are easier to implement (by a tiny bit).
So, we might get nested classes in some future version, but it's not decided yet.
You cannot do this in PHP. However, there are functional ways to accomplish this.
For more details please check this post:
How to do a PHP nested class or nested methods?
This way of implementation is called fluent interface: http://en.wikipedia.org/wiki/Fluent_interface
As per Xenon's comment to Anıl Özselgin's answer, anonymous classes have been implemented in PHP 7.0, which is as close to nested classes as you'll get right now. Here are the relevant RFCs:
Nested Classes (status: withdrawn)
Anonymous Classes (status: implemented in PHP 7.0)
An example to the original post, this is what your code would look like:
<?php
public class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
$this->profile = new class {
// Some code here for user profile
}
$this->history = new class {
// Some code here for user history
}
}
}
?>
This, though, comes with a very nasty caveat. If you use an IDE such as PHPStorm or NetBeans, and then add a method like this to the User class:
public function foo() {
$this->profile->...
}
...bye bye auto-completion. This is the case even if you code to interfaces (the I in SOLID), using a pattern like this:
<?php
public class User {
public $profile;
public function __construct() {
$this->profile = new class implements UserProfileInterface {
// Some code here for user profile
}
}
}
?>
Unless your only calls to $this->profile are from the __construct() method (or whatever method $this->profile is defined in) then you won't get any sort of type hinting. Your property is essentially "hidden" to your IDE, making life very hard if you rely on your IDE for auto-completion, code smell sniffing, and refactoring.
Since PHP version 5.4 you can force create objects with private constructor through reflection. It can be used to simulate Java nested classes. Example code:
class OuterClass {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function forkInnerObject($name) {
$class = new ReflectionClass('InnerClass');
$constructor = $class->getConstructor();
$constructor->setAccessible(true);
$innerObject = $class->newInstanceWithoutConstructor(); // This method appeared in PHP 5.4
$constructor->invoke($innerObject, $this, $name);
return $innerObject;
}
}
class InnerClass {
private $parentObject;
private $name;
private function __construct(OuterClass $parentObject, $name) {
$this->parentObject = $parentObject;
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function getParent() {
return $this->parentObject;
}
}
$outerObject = new OuterClass('This is an outer object');
//$innerObject = new InnerClass($outerObject, 'You cannot do it');
$innerObject = $outerObject->forkInnerObject('This is an inner object');
echo $innerObject->getName() . "\n";
echo $innerObject->getParent()->getName() . "\n";
You can't do it in PHP. PHP supports "include", but you can't even do that inside of a class definition. Not a lot of great options here.
This doesn't answer your question directly, but you may be interested in "Namespaces", a terribly ugly\syntax\hacked\on\top\of PHP OOP:
http://www.php.net/manual/en/language.namespaces.rationale.php
I think I wrote an elegant solution to this problem by using namespaces. In my case, the inner class does not need to know his parent class (like the static inner class in Java). As an example I made a class called 'User' and a subclass called 'Type', used as a reference for the user types (ADMIN, OTHERS) in my example. Regards.
User.php (User class file)
<?php
namespace
{
class User
{
private $type;
public function getType(){ return $this->type;}
public function setType($type){ $this->type = $type;}
}
}
namespace User
{
class Type
{
const ADMIN = 0;
const OTHERS = 1;
}
}
?>
Using.php (An example of how to call the 'subclass')
<?php
require_once("User.php");
//calling a subclass reference:
echo "Value of user type Admin: ".User\Type::ADMIN;
?>
You can, like this, in PHP 7:
class User{
public $id;
public $name;
public $password;
public $Profile;
public $History; /* (optional declaration, if it isn't public) */
public function __construct($id,$name,$password){
$this->id=$id;
$this->name=$name;
$this->name=$name;
$this->Profile=(object)[
'get'=>function(){
return 'Name: '.$this->name.''.(($this->History->get)());
}
];
$this->History=(object)[
'get'=>function(){
return ' History: '.(($this->History->track)());
}
,'track'=>function(){
return (lcg_value()>0.5?'good':'bad');
}
];
}
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();
It is waiting for voting as RFC
https://wiki.php.net/rfc/anonymous_classes
This page keeps coming up in my Internet searches on this subject so figured I should chime in even though this is an 8-year old post. The documentation for PHP5 demonstrates that anonymous classes can be defined within a class method. The object created can extend, implement, and even use other classes, interfaces, and traits. Consider the following OOP paradigm of factory object production. Similar to what #e-i-pi pointed out ...
class Factory {
/**
* Method to manufacture an inner-class object.
*
* #param string $args Arguments to be passed to
* the inner-class constructor.
*/
static function manufacture_object($args) {
/**
* Here's the definition of the inner-class.
*/
return new class($args) {
static $remembers = 'Nothing';
private $args;
function __construct($args) {
$this->$args = $args;
}
function says() {
return $this->args;
}
};
}
}
/**
* Create an inner-class object and have it do its thing.
*/
$mort = Factory::manufacture_object("Hello World!");
echo $mort->says(); // Echoes "Hello World!"
The objects are one-off, so one would expect the static values of the objects returned would not bind from one instance to another. After all, the anonymous class is unique from one object to another. However, late static binding works as one would otherwise expect from a nested class.
$mort = Factory::manufacture_object("I can remember that.");
$mort2 = Factory::manufacture_object("I'll live vicariously through you.");
$mort::$remembers = 'Something';
echo $mort2::$remembers; // Echoes "Something"
So, there you go: inner/nested classes and creation of their objects with static functionality has been possible since September 22, 2013 (right about the time this question was asked).
Put each class into separate files and "require" them.
User.php
<?php
class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
require_once('UserProfile.php');
require_once('UserHistory.php');
$this->profile = new UserProfile();
$this->history = new UserHistory();
}
}
?>
UserProfile.php
<?php
class UserProfile
{
// Some code here
}
?>
UserHistory.php
<?php
class UserHistory
{
// Some code here
}
?>
I've been reading through Effective Java by Joshua Bloch. I also develop in PHP and I wanted to implement the builder pattern outlined in item 2, but PHP doesn't have inner classes. Is there any way to achieve this pattern in PHP, keeping the constructor for the product private?
Since PHP does not support inner classes, there must be a public method on the product class that creates an instance of it. Consider the following PHP classes:
<?php
class NutritionalFactsBuilder {
private $sodium;
private $fat;
private $carbo;
/**
* It is preferred to call NutritionalFacts::createBuilder
* to calling this constructor directly.
*/
function __construct($s) {
$this->sodium = $s;
}
function fat($f) {
$this->fat = $f;
return $this;
}
function carbo($c) {
$this->carbo = $c;
return $this;
}
function getSodium() {
return $this->sodium;
}
function getFat() {
return $this->fat;
}
function getCarbo() {
return $this->carbo;
}
function build() {
return new NutritionalFacts($this);
}
}
class NutritionalFacts {
private $sodium;
private $fat;
private $carbo;
static function createBuilder($s) {
return new NutritionalFactsBuilder($s);
}
/**
* It is preferred to call NutritionalFacts::createBuilder
* to calling this constructor directly.
*/
function __construct(NutritionalFactsBuilder $b) {
$this->sodium = $b->getSodium();
$this->fat = $b->getFat();
$this->carbo = $b->getCarbo();
}
}
echo '<pre>';
var_dump(NutritionalFacts::createBuilder(10)->fat(23)->carbo(1)->build());
echo '</pre>';
?>
Note that in the above example the constructor of NutritionalFacts is public. Due to the constraints of the language, however, having a public constructor is not at all bad. Since one must call the constructor with a NutritionalFactsBuilder, there are only a limited number of ways to instantiate NutritionalFacts. Let's compare them:
// NutritionalFacts Instantiation #0
$nfb = new NutritionalFactsBuilder(10);
$nfb = $nfb->fat(23)->carbo(1);
$nf0 = new NutritionalFacts($nfb);
// NutritionalFacts Instantiation #1
$nfb = new NutritionalFactsBuilder(10);
$nf1 = $nfb->fat(23)->carbo(1)->build();
// NutritionalFacts Instantiation #2
$nf2 = NutritionalFacts::createBuilder(10)->fat(23)->carbo(1)->build();
// NutritionalFacts Instantiation #3
// $nf3 = (new NutritionalFactsBuilder(10))->fat(23)->carbo(1)->build();
To leverage function chaining to its fullest extent, "NutritionalFacts Instantiation #2" is the preferred usage.
"NutritionalFacts Instantiation #3" shows another nuance of PHP syntax; one cannot chain a method on a newly instantiated object. Update: In PHP 5.4.0, there is now support for the syntax in "NutritionalFacts Instantiation #3." I haven't tested it yet though.
Making the Constructor Private
You could make the constructor private, but I wouldn't recommend it. If the constructor were made private, a public, static factory method would be necessary, as in the following code snippet. Looking at the below code, we might as well make the constructor public instead of introducing indirection just to make the constructor private.
class NutritionalFacts {
private $sodium;
private $fat;
private $carbo;
static function createBuilder($s) {
return new NutritionalFactsBuilder($s);
}
static function createNutritionalFacts($builder) {
return new NutritionalFacts($builder);
}
private function __construct($b) {
$this->sodium = $b->getSodium();
$this->fat = $b->getFat();
$this->carbo = $b->getCarbo();
}
}
Immutability is good and definitely something to strive for, this applies to PHP as it does to any other language no matter what. Immutability gives you certainty that you do not have to fear that the instance suddenly mutates without you knowing.
That being said, there is an easy way to implement the builder pattern to build immutable objects even without inner classes (although available now with PHP 7).
The first important building block is a common base class for the actual immutable class and the builder. This allows them to access each others properties. Something that is also known as friend classes or solvable through extended access modifiers in other languages, something PHP does not have. Note that the clone ability is restricted, it makes no sense to clone immutable objects but more about the protected modifier later.
abstract class NutritionalFactData {
protected $sodium = 0;
protected $fat = 0;
protected $carbo = 0;
protected function __clone() {}
}
The immutable class is straight forward with stupid example getters and the default constructor. Note the final modifier for the class itself and that it is not aware of the builder class at all.
final class NutritionalFacts extends NutritionalFactData {
public function getSodium() {
return $this->sodium;
}
public function getFat() {
return $this->fat;
}
public function getCarbo() {
return $this->carbo;
}
}
Now the actual builder implementation. Note how we operate directly on an instance of the immutable class and that we simply clone it when the build method is called. This ensures that later calls to the setters of the builder will not alter the instances that were previously built and ensures that no receiver of such an instance has to take care of the cloning on their own.
final class NutritionalFactBuilder extends NutritionalFactData {
private $nutritional_facts;
public function __construct() {
$this->nutritional_facts = new NutritionalFacts;
}
public function build() {
return clone $this->nutritional_facts;
}
public function setSodium($sodium) {
$this->nutritional_facts->sodium = $sodium;
return $this;
}
public function setFat($fat) {
$this->nutritional_facts->fat = $fat;
return $this;
}
public function setCarbo($carbo) {
$this->nutritional_facts->carbo = $carbo;
return $this;
}
}
For completeness a usage example:
var_dump(
(new NutritionalFactBuilder)
->setSodium(21)
->setFat(42)
->build()
);
Here is the runnable example.
I think it is obvious that we can now implement as many builder implementations as we like. Not really needed for this example but we can think of other constructs where many more properties are involved. Like the car example given on (the very bad) builder pattern article of Wikipedia. We might want to have pre-configured builders for known car categories.
abstract class CarParts {}
final class Car extends CarParts {}
abstract class CarBuilder extends CarParts {
abstract public function build(): Car;
}
final class CompactCarBuilder extends CarBuilder {}
final class SportsCarBuilder extends CarBuilder {}
final class RaceCarBuilder extends CarBuilder {}
In the Gang of Four description of the Builder pattern, you'll find no requirement for an inner class. The key feature is the aggregate relationship between the Director and Builder interface that provide a "blueprint" for putting together a series of Product implementations.
You can find lots of examples of the PHP Builder pattern here:
http://www.php5dp.com/category/design-patterns/builder/
Cheers,
Bill