Regulating write access to object properties based on context - php

class SomeObject {
protected $foo,
$bar;
protected $context;
public function __construct($context) {
$this->context = $context;
}
public function setFoo($val) {
if ($this->context == 'public') {
throw new \Exception('It is impossible to modify foo property in public context!');
}
$this->foo = $val;
}
public function setBar($val) {
if ($this->context == 'api') {
throw new \Exception('It is impossible to modify bar property in API context!');
}
$this->bar = $val;
}
}
As you can see from this piece of "code" - object restricts setters depending on context value. This code is really hard to maintain. How can we rewrite it to make it beautiful and easy maintainable?
My thoughts are:
Make $context an object(s) implementing interface
isAllowed($object, $propertyName).
After making $context an object we have to thing about how can we store "restrictions" in $context object taking in mind there are a lot of different objects similar to SomeObject.
In every setter I should check $this->context->isAllowed($this, 'foo') - it looks not good. So, probably we want to add some "proxy" over SomeObject?
Passing $context to constructor also seems rather ugly for me.
What's your ideas about it?

Just two general observations:
You may want to segregate your classes into two parts: an immutable base class and a mutable extension:
class Foo {
protected $bar, $baz;
}
class MutableFoo extends Foo {
public function setBar($bar) {
$this->bar = $bar;
}
..
}
This easily solves the problem when the context is defined at object instantiation time and won't ever change. Instead of instantiating with a different context which determines the mutability, you simply instantiate a mutable or immutable version of the class.
If you still need more runtime checks, maybe simply using assertions is the best way to simplify the code:
public function setBar($bar) {
$this->assertCanSet('bar');
$this->bar = $bar;
}
protected function assertCanSet($property) {
if (!/* can set $property */) {
throw new Exception("Cannot set property $property");
}
}

Maybe on the construct, fill a list of restricted methods.
so, for instance :
class SomeObject {
protected $foo,
$bar;
protected $context;
protected $restrictedMethods;
public function __construct($context) {
$this->context = $context;
if($this->context == 'public') {
$this->restrictedMethods['setFoo'] = true;
} else if ($this->context == 'api') {
$this->restrictedMethods['setBar'] = true;
}
}
public function setFoo($val) {
if ($this->isRestricted('setFoo')) {
throw new \Exception('It is impossible to modify foo property in '.$this->context.' context!');
}
$this->foo = $val;
}
public function setBar($val) {
if ($this->isRestricted('setFoo')) {
throw new \Exception('It is impossible to modify bar property in '.$this->context.' context!');
}
$this->bar = $val;
}
protected function isRestricted($methodName) {
return array_key_exists($methodName, $this->restrictedMethods);
}
}

If you are trying to write good OOP, then "Interface Segregation" from the SOLID principle may be useful to you.
interface IBase
{
public function doMethod1();
public function doMethod2();
public function doMethod3();
}
interface IFoo extends IBase
{
public function setFoo($val);
}
interface IBar extends IBase
{
public function setBar($val);
}
function doWork(IBase $obj, $val)
{
$obj->doMethod1();
$obj->doMethod2();
$obj->doMethod3();
if ($obj instanceof IFoo) {
$obj->setFoo($val);
}
if ($obj instanceof IBar) {
$obj->setBar($val);
}
}
I doubt this example is exactly what you need, but I will use it to explain the basic idea.
A class should only have a "Single Responsibility". What that responsibility encompasses can vary however, so in general it is best to limit a class's functionality to a single area of concern as best you can.
If you want to follow "Liskov substitution", then throwing exceptions like that in your functions simply because the "context" was irrelevant, violates this principle.
Enter "Interface segregation":
By implementing an interface, you are (to a certain extent) guaranteeing to the caller of the implemented methods, that those methods will work. By excluding them, you are telling the caller that those methods don't exist.
In the example, the doWork function expects an instance of IBase, and safely calls the methods of that interface. After that, it runs introspection of the object to determine if other "applicable" methods are available.
The goal behind interface segregation is to limit the amount of unneeded features a class is forced to implement, so for you, if the context is public, it shouldn't need the setFoo method.

A clean solution would be to have an ObjectFactory class that creates different objects based on a $context parameter, and having two separate classes (with a common base class) that allows writing to the appropriate properties.
Please find below a possible implementation for your schema:
/**
* Base class that allows subclasses to define which properties are
* writable via setters. Subclasses must not add public setters,
* otherwise the mechanism within this class will not work; subclasses
* can add protected setters, though
*/
class PropertyRestricter {
// only properties listed here are writable
// to be initialised by subclasses
protected $writableProperties;
public function __construct() {
// default implementation allows no writable properties
$this->writableProperties = array();
}
public function __call($method, $arguments) {
$matches = false;
// check if a setter was called, extract the property name
// there needs to be at least one argument to the setter
if(count($arguments) && preg_match('/^set([A-Z][a-zA-Z0-9_]+)$/',$matches)) {
$propName = $matches[1];
$propName[0] = strtolower($propName[0]);
// update the property with the given value
// or throw an exception if the property is not writable
if(is_array($this->writableProperties) && in_array($propName, $this->writableProperties)) {
$this->{$propName} = $arguments[0];
} else {
throw new Exception(get_class() . ": $propName is not writable");
}
} else {
// not a setter, nor a public method
// maybe display a user error
}
}
}
/**
* Common properties for the actual classes
*/
class BaseObject extends PropertyRestricter {
protected $foo, $bar;
}
class PublicObject extends BaseObject {
public function __construct() {
parent::__construct();
$this->writableProperties = array('foo');
}
}
class APIObject extends BaseObject {
public function __construct() {
parent::__construct();
$this->writableProperties = array('bar');
}
}
class ObjectFactory {
public function createObject($context) {
switch($context) {
case 'public': return new PublicObject();
case 'api': return new APIObject();
default: return null;
}
}
}
The root of the objects is the PropertyRestricter class that allows subclasses to define which properties are writable. It makes use of the magic method __call() in order to be able to intercept setter calls and to validate the attempt to write to the property. However please note that this works only if subclasses don't add public setters for their properties.
The next level is the BaseObject class, which only defines the two properties, in order to reduce code redundancy.
The last level contains the two classes that get instantiated by the ObjectFactory: PublicObject, 'APIObject. These classes simply initialise thewritablePropertiesarray, as the rest of the work is done by thePropertyRestricter` class.
This is also a scalable solution, as it allows adding as many properties and subclasses as needed, each subclass defining its property writing rules.
Also the property update within the __call() method can be customised, I implemented it in the simplest way by directly setting the property. Actual setters can be used in subclasses and __call() can be updated to call the setters, with the mention that the setters need to be protected in order for the mechanism to work.

Related

Do all properties need to be protected?

Hello I was wondering why some popular PHP libraries make all properties protected, then add get and set methods for them like:
protected
$a = false;
public function getA(){
return $this->a;
}
public function setA($value){
$this->a = (bool)$value;
}
What's the benefit of this and why not simply make the property public?
OOP real world scenario:
Imagine you have a class Vehicles and they have (protected) wheels. You have different Vehicles with wheels, but to addWheel to a Bike, is different from addWheel to an Aircraft.
Code advantage:
Using getter and setter, you can use typehinting.
Compare those snippets:
class Car {
public $wheels;
}
$bmw = new Car;
$bmw->wheels = 'piece of paper';
The above code let's you add anything as a wheel, but can you use a piece of paper as a wheel?
Now with getter and setter:
class Car {
protected wheels;
public function __construct() {
$this->wheels = new ArrayIterator;
}
public function addWheel(Wheel $wheel) {
$this->wheels->add($wheel);
return $this;
}
public function removeWheel(Wheel $wheel) {
$this->wheels->remove($wheel);
return $this;
}
}
class Wheel {
}
$bmw = new Car;
$bmw->addWheel('piece of paper'); // <-- throws an error!
// paper cannot be used as a wheel
$bmw->addWheel(new Wheel); // <-- good :-)
More code, to be more straightforward. Imagine you have RearWheels and FrontWheels:
class Wheel {
}
class FrontWheel extends Wheel {
}
class RearWheel extends Wheel {
}
class Car {
protected wheels;
public function __construct() {
$this->wheels = new ArrayIterator;
}
public function addWheel(Wheel $wheel) {
// check for already existing Wheels here.
// Pseudo Code:
if (wheels typeof RearWheel > 2) {
throw new Exception('cannot add more than 2 RearWheels to the Car');
}
return $this;
}
public function removeWheel(Wheel $wheel) {
$this->wheels->remove($wheel);
return $this;
}
}
It allows you to add custom setters and getters in case you want to add extra functionality, and also is sometimes used to track the "dirty" state (if an object has been changed since it was loaded from the DB).
It´s also due to the fact that PHP does not have a "native" syntax for read only properties.
Addition:
I didn´t quite get my point across in my original answer that its in many cases due to how PHP works.
Consider this PHP example where we use metaprogramming to dynamically call custom setters.
class Foo {
protected $bar;
// use some metaprogramming magic to automatically call setter
public function __set($key, $val){
$setter = "set".ucfirst($key);
if (method_exists($this, $setter)){
$this->$setter($val);
}
else {
throw new Exception("$key value cannot be set, looking for $setter in ".get_class($this));
}
}
// enforce the Type of bar
public function setBar(String $val){
$this->bar = $val;
}
}
$foo = new Foo();
// this should fail
$foo->bar = 12;
// Catchable fatal error: Argument 1 passed to Foo::setBar() must be an instance of String, integer given
What I´m trying to get at here is that you are not a Bad Programmer for having public properties, but rather that most PHP frameworks dodge around the PHP object model that requires properties to be protected for inheritance and reflection to work in many cases.
Popular libraries which support their classes to be extended or inherited make their fields protected. This is concept of OOP that there must be abstraction and encapsulation in terms of data. Everyone shall not be allowed to directly access the data. Only member of a class shall access its data. This is reason fields are marked protected so that only inherited class's objects or same class's objects shall have access to data. All others must use get and set methods for reaching data. Keeping the code managed, clean and safe.

Save object clone as protected property

I intend to create a clone of an object's parent within the constructor of that parent. In short:
class ParentClass {
protected $property;
public function __construct() {
$this->property = clone $this;
}
}
class ChildClass extends ParentClass {
}
This works all fine, yet the problem with this code is the protected property getting populated with an instance of the ChildClass, in case the ChildClass is instantiated. I would however like it to be an instance of the ParentClass regardless of the class $this refers to.
I could of course combine debug_backtrace and new self() (in order to avoid endless recursion of constructor invocations) and assign the resulting ParentClass instance to the property, though such a soluation is verbose, as debug backtrace only returns string names of caller classes and methods.
Lastly, I could combine new self() and the provision of an argument to the instantiation of the object indicating if a "new self" should be created, but I dislike the solution because of its ugliness and redundancy.
Is there a way in PHP to find a "clone of self"?
As discussed in the comments, I think the reason this pattern is not working for you is that you have a poorly designed object hierarchy. In the example, ChildClass is a "type of" ParentClass, but also internally references a copy of ParentClass to do some delegated work.
From the comments, what you have must look something like this:
class BasicLogger {
protected $delegated_logger;
public function __construct() {
// initialise $this->delegated_logger somehow
}
public function logMessage($message, $flags) {
{
$prepared_message = $this->prepareMessage($message, $flags);
$this->deliverMessage($prepared_message);
}
private function prepareMessage($message, $flags) {
// implementation here
}
protected function deliverMessage($prepared_message) {
// implementation here
}
}
class MailLogger extends BasicLogger {
protected function deliverMessage($prepared_message) {
// different implementation here
if ( $mail_sending_failed ) {
$this->delegated_logger->logMessage('Oops, MailLogger failed...');
}
}
}
However, BasicLogger is actually performing multiple roles in the object hierarchy:
defining the interface that all loggers should adhere to (here represented as a single logMessage method)
providing a shared implementation of prepareMessage that all loggers will use, and an implementation of logMessage that depends on it plus a deliverMessage function
providing a specific implementation of deliverMessage that will be completely over-written by child classes
providing a mechanism for complex implementations to delegate to simpler implementations, without a way of distinguishing between the two
The first three roles should be separated into an interface, an abstract base class, and a simple implementation:
interface Logger {
public function logMessage($message, $flags = null);
}
abstract class BaseLogger implements Logger {
public function logMessage($message, $flags = null) {
{
$prepared_message = $this->prepareMessage($message, $flags);
$this->deliverMessage($prepared_message);
}
private function prepareMessage($message, $flags) {
// implementation here
}
abstract protected function deliverMessage($prepared_message);
}
class BasicTextLogger extends BaseLogger {
protected function deliverMessage($prepared_message) {
// implementation here
}
}
You can then use instances of BasicTextLogger wherever you need, including in other implementations of BaseLogger.
You might want to put the logic of having a delegated logger (the 4th role of my BasicLogger above) into another class for reuse. BasicTextLogger shouldn't inherit this behaviour, or you'll end up needing to provide a logger to a logger to a logger to a logger, ad infinitum.
abstract class ComplexLogger extends BaseLogger {
protected $delegated_logger;
public function __construct( Logger $delegated_logger ) {
if ( $delegated_logger instanceOf ComplexLogger ) {
throw new Exception('Attempted to delegate one complex logger to another; danger of recursion, so disallowed.');
} else {
$this->delegated_logger = $delegated_logger;
}
}
}
class MailLogger extends ComplexLogger {
protected function deliverMessage($prepared_message) {
// different implementation here
if ( $mail_sending_failed ) {
$this->delegated_logger->logMessage('Oops, MailLogger failed...');
}
}
}
This then allows you to perform Dependency Injection to provide your complex logger with a simple logger to delegate to:
$my_logger = new MailLogger( new BasicTextLogger() );
$my_logger->logMessage('Hello World!');
This may seem like a lot of different classes and interfaces, but each now has a clear responsibility. You could put the whole $delegated_logger logic into MailLogger, but you'd have to copy and paste it if you had another complex logger later. You might also be able to ignore the Logger interface, and just type-hint for classes deriving from the BaseLogger class, but it's possible you'll want an implementation that doesn't use prepareMessage at all - for instance, a DoNothingLogger.

PHP Builder pattern without inner classes

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

How Many Static Methods is Too Many For One Class?

UPDATE: Rephrasing the question to ask, 'are there too many' static methods (I realize that right now there are only 4 but I originally started with 2) in this class structure? If so, any suggestions on how to refactor these classes to use some sort of Finder class so that I can remove the static functions from the Model classes?
I have the following abstract class:
abstract class LP_Model_Abstract
{
protected static $_collectionClass = 'LP_Model_Collection';
protected $_row = null;
protected $_data = array();
public function __construct($row = null)
{
$this->_row = $row;
}
public function __get($key)
{
if(method_exists($this, '_get' . ucfirst($key)))
{
$method = '_get' . ucfirst($key);
return $this->$method();
}
elseif(isset($this->_row->$key))
{
return $this->_row->$key;
}
else
{
foreach($this->_data as $gateway)
{
if(isset($gateway->$key))
{
return $gateway->$key;
}
}
}
}
public function __set($key, $val)
{
if(method_exists($this, '_set' . ucfirst($key)))
{
$method = '_set' . ucfirst($key);
return $this->$method($val);
}
elseif(isset($this->_row->$key))
{
$this->_row->$key = $val;
return $this->_row->$key;
}
else
{
foreach($this->_data as $gateway)
{
if(isset($this->_data[$gateway]->$key))
{
$this->_data[$gateway]->$key = $val;
return $this->_data[$gateway]->$key;
}
}
}
}
public function __isset($key)
{
return isset($this->_row->$key);
}
public function save()
{
$this->_row->save();
}
abstract public static function get($params);
abstract public static function getCollection($params = null);
abstract public static function create($params);
}
And then this class which provides additional functionality for class table inheritance schemes (where type is important in determining additional functionality in a factory fashion):
abstract class LP_Model_Factory_Abstract extends LP_Model_Abstract
{
protected static $_collectionClass = 'LP_Model_Collection_Factory';
abstract public static function factory($row);
}
These ultimately result in the following type of class declaration:
class Model_Artifact extends LP_Model_Factory_Abstract
{
protected static $_artifactGateway = 'Model_Table_Artifact';
public static function create($params)
{
}
public static function get($params)
{
$gateway = new self::$_artifactGateway();
$row = $gateway->fetchArtifact($params);
return self::factory($row);
}
public static function getCollection($params = null)
{
$gateway = new self::$_artifactGateway();
$rowset = $gateway->fetchArtifacts($params);
$data = array(
'data' => $rowset,
'modelClass' => __CLASS__
);
return new self::$_collectionClass($data);
}
public static function factory($row)
{
$class = 'Model_Artifact_' . $row->fileType;
}
}
When do you know that you have too many static methods in a class? And how would you refactor the existing design so that the static methods are perhaps encapsulated in some sort of Finder class?
I'd have to agree with Brubaker and add that to my thinking it isn't the number of methods so much as the functionality of said methods. If you start thinking that your class has to many methods (static or otherwise) then you might find they can be re-grouped and refactored into a more intuitive architecture.
The first indicator I use when determining if I have to many static methods is if the methods functionality is not stateless. If the static methods change the state of the object they reside in, they probably shouldn't be static.
I agree with BaileyP and I'll add my couple of pennies:
I always work with the idea that a class should have a single reason for existing; it should have one job that it does, and it should do it well. After deciding that, and figuring out what the interface to that class should be, I go through and mark any functions that don't change the state of an instance of the class as static.
If you want to build reusable and testable code, you should avoid static methods altogether. Code which calls static methods (or constructors of non-data-like classes) cannot be tested in isolation.
Yes, you will have to pass around alot more objects if you eliminate static methods. This is not necessarily a bad thing. It forces you to think about the boundaries and cooperation between your components in a disciplined way.
Personally I find that any number of static methods are a sign of trouble. If your class has instance methods and static methods, then most likely you could split the class into two separate entities and change the static methods to instance methods.
Think of a class as a special kind of object, with the distinctive property that it is global by nature. Since it's a global variable, it implies a very strong level of coupling, so you would want to reduce any references to it. Static members will need to be referred, meaning that your code will get a strong level of coupling to the class.
I'll throw in my 2 cents.
First of all, I'll agree that setting some sort of arbitrary limit is not helpful, such as "Once I have more than 10 statics in a class that's too many!". Refactor when it makes sense but don't start doing it just because you've hit some imaginary boundary.
I wouldn't 100% agree with Brubaker's comment about stateful vs. stateless - I think the issue is more about classes vs instances. Because a static method can change the value of another static property which is a stateful change.
So, think of it like this - if the method/property is of or pertaining to the class, then it should probably be static. If the method/property is of or pertaining to an instance of the class, it should not be static.

Best practices to test protected methods with PHPUnit

I found the discussion on Do you test private method informative.
I have decided, that in some classes, I want to have protected methods, but test them.
Some of these methods are static and short. Because most of the public methods make use of them, I will probably be able to safely remove the tests later. But for starting with a TDD approach and avoid debugging, I really want to test them.
I thought of the following:
Method Object as adviced in an answer seems to be overkill for this.
Start with public methods and when code coverage is given by higher level tests, turn them protected and remove the tests.
Inherit a class with a testable interface making protected methods public
Which is best practice? Is there anything else?
It seems, that JUnit automatically changes protected methods to be public, but I did not have a deeper look at it. PHP does not allow this via reflection.
If you're using PHP5 (>= 5.3.2) with PHPUnit, you can test your private and protected methods by using reflection to set them to be public prior to running your tests:
protected static function getMethod($name) {
$class = new ReflectionClass('MyClass');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
public function testFoo() {
$foo = self::getMethod('foo');
$obj = new MyClass();
$foo->invokeArgs($obj, array(...));
...
}
teastburn has the right approach. Even simpler is to call the method directly and return the answer:
class PHPUnitUtil
{
public static function callMethod($obj, $name, array $args) {
$class = new \ReflectionClass($obj);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($obj, $args);
}
}
You can call this simply in your tests by:
$returnVal = PHPUnitUtil::callMethod(
$this->object,
'_nameOfProtectedMethod',
array($arg1, $arg2)
);
You seem to be aware already, but I'll just restate it anyway; It's a bad sign, if you need to test protected methods. The aim of a unit test, is to test the interface of a class, and protected methods are implementation details. That said, there are cases where it makes sense. If you use inheritance, you can see a superclass as providing an interface for the subclass. So here, you would have to test the protected method (But never a private one). The solution to this, is to create a subclass for testing purpose, and use this to expose the methods. Eg.:
class Foo {
protected function stuff() {
// secret stuff, you want to test
}
}
class SubFoo extends Foo {
public function exposedStuff() {
return $this->stuff();
}
}
Note that you can always replace inheritance with composition. When testing code, it's usually a lot easier to deal with code that uses this pattern, so you may want to consider that option.
I'd like to propose a slight variation to getMethod() defined in uckelman's answer.
This version changes getMethod() by removing hard-coded values and simplifying usage a little. I recommend adding it to your PHPUnitUtil class as in the example below or to your PHPUnit_Framework_TestCase-extending class (or, I suppose, globally to your PHPUnitUtil file).
Since MyClass is being instantiated anyways and ReflectionClass can take a string or an object...
class PHPUnitUtil {
/**
* Get a private or protected method for testing/documentation purposes.
* How to use for MyClass->foo():
* $cls = new MyClass();
* $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
* $foo->invoke($cls, $...);
* #param object $obj The instantiated instance of your class
* #param string $name The name of your private/protected method
* #return ReflectionMethod The method you asked for
*/
public static function getPrivateMethod($obj, $name) {
$class = new ReflectionClass($obj);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
// ... some other functions
}
I also created an alias function getProtectedMethod() to be explicit what is expected, but that one's up to you.
I think troelskn is close. I would do this instead:
class ClassToTest
{
protected function testThisMethod()
{
// Implement stuff here
}
}
Then, implement something like this:
class TestClassToTest extends ClassToTest
{
public function testThisMethod()
{
return parent::testThisMethod();
}
}
You then run your tests against TestClassToTest.
It should be possible to automatically generate such extension classes by parsing the code. I wouldn't be surprised if PHPUnit already offers such a mechanism (though I haven't checked).
I'm going to throw my hat into the ring here:
I've used the __call hack with mixed degrees of success.
The alternative I came up with was to use the Visitor pattern:
1: generate a stdClass or custom class (to enforce type)
2: prime that with the required method and arguments
3: ensure that your SUT has an acceptVisitor method which will execute the method with the arguments specified in the visiting class
4: inject it into the class you wish to test
5: SUT injects the result of operation into the visitor
6: apply your test conditions to the Visitor's result attribute
You can indeed use __call() in a generic fashion to access protected methods. To be able to test this class
class Example {
protected function getMessage() {
return 'hello';
}
}
you create a subclass in ExampleTest.php:
class ExampleExposed extends Example {
public function __call($method, array $args = array()) {
if (!method_exists($this, $method))
throw new BadMethodCallException("method '$method' does not exist");
return call_user_func_array(array($this, $method), $args);
}
}
Note that the __call() method does not reference the class in any way so you can copy the above for each class with protected methods you want to test and just change the class declaration. You may be able to place this function in a common base class, but I haven't tried it.
Now the test case itself only differs in where you construct the object to be tested, swapping in ExampleExposed for Example.
class ExampleTest extends PHPUnit_Framework_TestCase {
function testGetMessage() {
$fixture = new ExampleExposed();
self::assertEquals('hello', $fixture->getMessage());
}
}
I believe PHP 5.3 allows you to use reflection to change the accessibility of methods directly, but I assume you'd have to do so for each method individually.
I suggest following workaround for "Henrik Paul"'s workaround/idea :)
You know names of private methods of your class. For example they are like _add(), _edit(), _delete() etc.
Hence when you want to test it from aspect of unit-testing, just call private methods by prefixing and/or suffixing some common word (for example _addPhpunit) so that when __call() method is called (since method _addPhpunit() doesn't exist) of owner class, you just put necessary code in __call() method to remove prefixed/suffixed word/s (Phpunit) and then to call that deduced private method from there. This is another good use of magic methods.
Try it out.
Alternative.The code below is provided as an example.
Its implementation can be much broader.
Its implementation that will help you test private methods and replacing a private property .
<?php
class Helper{
public static function sandbox(\Closure $call,$target,?string $slaveClass=null,...$args)
{
$slaveClass=!empty($slaveClass)?$slaveClass:(is_string($target)?$target:get_class($target));
$target=!is_string($target)?$target:null;
$call=$call->bindTo($target,$slaveClass);
return $call(...$args);
}
}
class A{
private $prop='bay';
public function get()
{
return $this->prop;
}
}
class B extends A{}
$b=new B;
$priv_prop=Helper::sandbox(function(...$args){
return $this->prop;
},$b,A::class);
var_dump($priv_prop);// bay
Helper::sandbox(function(...$args){
$this->prop=$args[0];
},$b,A::class,'hello');
var_dump($b->get());// hello
You can use Closure as in the code below
<?php
class A
{
private string $value = 'Kolobol';
private string $otherPrivateValue = 'I\'m very private, like a some kind of password!';
public function setValue(string $value): void
{
$this->value = $value;
}
private function getValue(): string
{
return $this->value . ': ' . $this->getVeryPrivate();
}
private function getVeryPrivate()
{
return $this->otherPrivateValue;
}
}
$getPrivateProperty = function &(string $propName) {
return $this->$propName;
};
$getPrivateMethod = function (string $methodName) {
return Closure::fromCallable([$this, $methodName]);
};
$objA = new A;
$getPrivateProperty = Closure::bind($getPrivateProperty, $objA, $objA);
$getPrivateMethod = Closure::bind($getPrivateMethod, $objA, $objA);
$privateByLink = &$getPrivateProperty('value');
$privateMethod = $getPrivateMethod('getValue');
echo $privateByLink, PHP_EOL; // Kolobok
$objA->setValue('Zmey-Gorynich');
echo $privateByLink, PHP_EOL; // Zmey-Gorynich
$privateByLink = 'Alyonushka';
echo $privateMethod(); // Alyonushka: I'm very private, like a some kind of password!
I made a class for invoking easily private methods (static and non-static) for unit-testing purposes:
class MethodInvoker
{
public function invoke($object, string $methodName, array $args=[]) {
$privateMethod = $this->getMethod(get_class($object), $methodName);
return $privateMethod->invokeArgs($object, $args);
}
private function getMethod(string $className, string $methodName) {
$class = new \ReflectionClass($className);
$method = $class->getMethod($methodName);
$method->setAccessible(true);
return $method;
}
}
Example of usage:
class TestClass {
private function privateMethod(string $txt) {
print_r('invoked privateMethod: ' . $txt);
}
}
(new MethodInvoker)->invoke(new TestClass, 'privateMethod', ['argument_1']);

Categories