Let say i have this class
class Test
{
method_a(){ return $this;}
method_b(){ return $this;}
method_c(){ return $this;}
}
$obj = new Test();
$obj->method_a()->method_b();
$obj->method_a()->method_c();
$obj->method_b()->method_c(); //i want to disallow this
How can i disallow method_b() chaining with method_c()
Edited:
Calling $obj->method_b() and follow by $obj->method_c() also disallow because i only want method_b chaining with method_a and method_c with other method
there are two possibilitys i can think of. the first is to not retun $this from method_b(). that would prevent chaining method_c() - but everything else, too, so this doesn't seem to be what you're looking for.
the second one is kind of ugly, but might work as intended: add another private variable $last_method to your class and set that in every called method (to the methods name or some kind of id). that way, when calling method_c(), you could check if the last called method was method_b() - and if so, throw an exception (or whatever you'd like to do in such a case). note that this solution would also prevent from calling method_b() and method_c() consecutive on the same object without chaining - so this might not be 100% what you're looking for.
You could use a State Pattern and introduce State objects for each possible state in Test. When someone calls Method B, you change the state of Test internally to the StateB class and when someone calls Method C then you can raise an Exception.
See http://sourcemaking.com/design_patterns/state
Related
I have a simple requirement where I want to allow a Class (Laravel Model) to define whether a controller should check whether it's allowed to be deleted or not based on a class variable like so
public $verify_delete = true;
Currently if this variable is set I must add the following method;
public function deletable()
{
$deletable = true;
if ($this->has_children()) {
$deletable = false;
}
// ...
return $deletable;
}
The first variable is simply an indication the code running this class should also run the deletable() method.
So what I want is a way to force a developer to implement the deletable() method if they set the $verify_delete to true, and throw an error if they have not done so. I don't know much about traits and implements at this stage, so I'm not sure if they're the right route to go down.
If this is not the best method to achieve what I'm after I'm all ears.
You should create a trait called Deletable or Removeable or something along these lines and implement this delete method there and then each model that can be deleted will use this trait. Not exactly what you wanted but it is the right approach.
Or
You can create an interface which will have this method inside and then each class (model) which implements this interface will have to implement the method, the only difference is that implementation may vary depending on the model from others
You question is this: can I detect if a method is callable in a class, when a specific property is set...
Other (very wise) solutions are suggesting interfaces and traits... A perfectly fine idea and a really good way to look at things.
But you can think of something like this as well:
public function __construct() {
if (true === $this->verify_delete && !method_exists($this, 'deletable')) {
throw new \Exception('Hi developer! You forgot something...');
}
}
Explanation:
on the constructor you check if the property is true.
if the method does not exist, let's throw an exception
I think this does exactly as you ask: force the developer to implement the deletable method, when the property is set.
But to be honest, I really like those traits as well... I just wanted to give you an alternative that does the trick as well if you are not too comfortable with techniques likes interfaces, traits, etc.
Suppose I have a class with a private property and associated public getter and setter. I want to test with PHPUnit that the property gets the correct value after the setter has been used or that the getter returns the correct property.
Of course I can test the setter by using the getter to see that the object is storing the correct value, and vice versa for testing the getter. However, this doesn't guarantee that the private property is the one being set.
Say I had the following class. I created a property, getter and setter. But I made a typo in the property name, so the getter and the setter don't actually manipulate the property they're meant to manipulate
class SomeClass
{
private
$mane = NULL; // Was supposed to be $name but got fat-fingered!
public function getName ()
{
return ($this -> name);
}
public function setName ($newName)
{
$this -> name = $newName;
return ($this);
}
}
If I run the following test
public function testSetName ()
{
$this -> object -> setName ('Gerald');
$this -> assertTrue ($this -> object -> getName () == 'Gerald');
}
I would get a pass. However, something very bad has actually happened that I don't want. When setName() is called, it actually creates a new property in the class with the name I thought my private property had, only the one that the setter creates is public! I can demonstrate that with the following code:
$a = new SomeClass;
$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);
It would output:
string(6) "gerald"
string(6) "gerald"
Is there any way I can access the private properties from PHPUnit so I can write tests that make sure that the properties I think are being get and set actually really are being get and set?
Or is there some other thing I should be doing in a test to catch problems like this without trying to get access to the private state of the object under test?
You can also use Assert::assertAttributeEquals('value', 'propertyName', $object).
See https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
For testing properties, I'd make the same arguments I make then talking about testing private methods.
You usually don't want to do this.
It's about testing observable behavior.
If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.
So, all in all, you lose the value of you test suite!
Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.
Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.
If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable
Finding unused properties to catch bugs / issues like this one:
The PHP Mess Detector As a UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
This will generate two warnings for you because the variables are never accessed
I just want to point out one thing. Let's forget about private fields for a moment and focus on what client of your class cares about. Your class exposes a contract, in this case - ability to alter and retrieve name (via getter and setter). Expected functionality is simple:
when I set name with setName to "Gerald", I expect to get "Gerald" when I call getName
That's all. Client won't (well, shouldn't!) care about internal implementation. Whether you used private field name, hashset or called web service via dynamically generated code - doesn't matter for client. The bug you are currently experiencing, from user point of view - is not a bug at all.
Whether PHPUnit allows you to test private variables - I don't know. But from unit-testing perspective, you shouldn't do that.
Edit (in response to comment):
I understand your concerns about possible exposure of internal state, however I don't think unit testing is the right tool to deal with that. You can come up with a lot of possible scenarios how something might do something else which wasn't planned. Unit tests are by no means cure for all and shouldn't be used as such.
I agree with the others that in general you want to avoid accessing privates in your tests, but for the cases where you need to, you can use reflection to read and write the property.
I want to exclude all inherited methods from trait(s) from the list that are not overriden in a class
So how to know if a class member is inherited from trait?
Yes, I can check it like this:
if ($trait->hasMethod($methodName)
|| $ref->getTraitAliases()[$methodName] !== null)
{
//
}
But what if the trait method is overriden in a class? How to know it?
One way is to check if method bodies are similar, if so, i may exclude it,
but is there a better way to achieve this?
A simpler way to do this is ReflectionMethod::getFileName(). This will return the file name of the trait, not the class.
For the exotic case where trait and class are in the same file, one can use ReflectionMethod::getStartLine(), and compare this with start and end line of trait and class.
For the exotic case where trait and class and method are all on the same line.. oh please!
I am sorry but the accepted answer by Alma Do is completely wrong.
This solution cannot work even if you overcome the problem of spl_object_hash() values being recycled. This problem can be overcome by refactoring the get*MethodRefs() functions into one function that computes both results and ensures that the ReflectionMethod objects for the trait methods still exist when the analogous objects for the class methods are created. This prevents recycling of spl_object_hash() values.
The problem is, the assumption that "PHP will use same instance for trait method" is completely false, and the appearance of that happening resulted precisely from "lucky" spl_object_hash() recycling. The object returned by $traitRef->getMethod('someName') will always be distinct from the object returned by $classRef->getMethod('someName'), and so will be the corresponding instances of ReflectionMethod in collections returned by ->getMethods(), regardless of whether method someName() is overriden in the class or not. These objects will not only be distinct, they won't even be "equal": the ReflectionMethod instance obtained from $traitRef will have the name of the trait as the value of its class property, and the one obtained from $classRef will have the name of the class there.
Fiddle: https://3v4l.org/CqEW3
It would seem that only parser-based approaches are viable then.
Important notes
This is only because of "academical" interest, in real situation you should not care about - from where method was derived as it contradicts the idea of traits, e.g. transparent substitution.
Also, because of how traits are working, any kind of such manipulations might be considered as "hacky", so behavior may differ across different PHP versions and I would not suggest to rely on that.
Distinction: difficulties
In reflection for PHP, there is getTraits() methods which will return ReflectionClass instance, pointing to reflection of trait. This may be used to fetch all methods, declared in traits, which are used in the class. However - no, it will not help in your question as there will be not possible to distinct which methods were then overridden in the class.
Imagine that there is trait X with methods foo() and bar() and there is class Z with method bar(). Then you will be able to know that methods foo() and bar() are declared in trait, but if you will try to use getMethods() on class Z - you will obviously get both foo() and bar() as well. Therefore, directly you can not distinct the case.
Distinction: work-aroud
However, yes, there is a way to still make it work. First way - is - like you've mentioned - try to investigate source code. It's quite ugly, but in the very end, this is the only 100% reliable way to resolve the matter.
But - no, there is another, "less ugly" way - to inspect instances on ReflectionMethod classes, that are created for class/traits methods. It happens that PHP will use same instance for trait method, but will override that one which is for the method, declared in class.
This "inspection" can be done with spl_object_hash(). Simple setup:
trait x
{
public function foo()
{
echo 'Trait x foo()';
}
public function bar()
{
echo 'Trait x bar()';
}
}
class z
{
use x;
public function foo()
{
echo 'Class foo()';
}
}
And now, to fetch hashes for both cases:
function getTraitMethodsRefs(ReflectionClass $class)
{
$traitMethods = call_user_func_array('array_merge', array_map(function(ReflectionClass $ref) {
return $ref->getMethods();
}, $class->getTraits()));
$traitMethods = call_user_func_array('array_merge', array_map(function (ReflectionMethod $method) {
return [spl_object_hash($method) => $method->getName()];
}, $traitMethods));
return $traitMethods;
}
function getClassMethodsRefs(ReflectionClass $class)
{
return call_user_func_array('array_merge', array_map(function (ReflectionMethod $method) {
return [spl_object_hash($method) => $method->getName()];
}, $class->getMethods()));
}
In short: it just fetches all methods from class trait (first function) or class itself (second function) and then merges results to get key=>value map where key is object hash and value is method name.
Then we need to use that on same instance like this:
$obj = new z;
$ref = new ReflectionClass($obj);
$traitRefs = getTraitMethodsRefs($ref);
$classRefs = getClassMethodsRefs($ref);
$traitOnlyHashes = array_diff(
array_keys($traitRefs),
array_keys($classRefs)
);
$traitOnlyMethods = array_intersect_key($traitRefs, array_flip($traitOnlyHashes));
So result, $traitOnlyMethods will contain only those methods, which are derived from trait.
The corresponding fiddle is here. But pay attention to results - they may be different from version to version, like in HHVM it just doesn't work (I assume because of how spl_object_hash is implemented - an either way, it is not safe to rely on it for object distinction - see documentation for the function).
So, TD;DR; - yes, it can be (somehow) done even without source code parsing - but I can not imagine any reason why it will be needed as traits are intended to be used to substitute code into the class.
Suppose I have a class with a private property and associated public getter and setter. I want to test with PHPUnit that the property gets the correct value after the setter has been used or that the getter returns the correct property.
Of course I can test the setter by using the getter to see that the object is storing the correct value, and vice versa for testing the getter. However, this doesn't guarantee that the private property is the one being set.
Say I had the following class. I created a property, getter and setter. But I made a typo in the property name, so the getter and the setter don't actually manipulate the property they're meant to manipulate
class SomeClass
{
private
$mane = NULL; // Was supposed to be $name but got fat-fingered!
public function getName ()
{
return ($this -> name);
}
public function setName ($newName)
{
$this -> name = $newName;
return ($this);
}
}
If I run the following test
public function testSetName ()
{
$this -> object -> setName ('Gerald');
$this -> assertTrue ($this -> object -> getName () == 'Gerald');
}
I would get a pass. However, something very bad has actually happened that I don't want. When setName() is called, it actually creates a new property in the class with the name I thought my private property had, only the one that the setter creates is public! I can demonstrate that with the following code:
$a = new SomeClass;
$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);
It would output:
string(6) "gerald"
string(6) "gerald"
Is there any way I can access the private properties from PHPUnit so I can write tests that make sure that the properties I think are being get and set actually really are being get and set?
Or is there some other thing I should be doing in a test to catch problems like this without trying to get access to the private state of the object under test?
You can also use Assert::assertAttributeEquals('value', 'propertyName', $object).
See https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
For testing properties, I'd make the same arguments I make then talking about testing private methods.
You usually don't want to do this.
It's about testing observable behavior.
If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.
So, all in all, you lose the value of you test suite!
Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.
Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.
If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable
Finding unused properties to catch bugs / issues like this one:
The PHP Mess Detector As a UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
This will generate two warnings for you because the variables are never accessed
I just want to point out one thing. Let's forget about private fields for a moment and focus on what client of your class cares about. Your class exposes a contract, in this case - ability to alter and retrieve name (via getter and setter). Expected functionality is simple:
when I set name with setName to "Gerald", I expect to get "Gerald" when I call getName
That's all. Client won't (well, shouldn't!) care about internal implementation. Whether you used private field name, hashset or called web service via dynamically generated code - doesn't matter for client. The bug you are currently experiencing, from user point of view - is not a bug at all.
Whether PHPUnit allows you to test private variables - I don't know. But from unit-testing perspective, you shouldn't do that.
Edit (in response to comment):
I understand your concerns about possible exposure of internal state, however I don't think unit testing is the right tool to deal with that. You can come up with a lot of possible scenarios how something might do something else which wasn't planned. Unit tests are by no means cure for all and shouldn't be used as such.
I agree with the others that in general you want to avoid accessing privates in your tests, but for the cases where you need to, you can use reflection to read and write the property.
Is there any security problem with dynamically calling a method in a class from user input. For example:
<?php
class A {
public function foo() {
return true;
}
}
$obj = new A();
$method = $_GET['method'];
$obj->$method();
I am aware that the user will be able to call any method within A, and I am fine with that. I am just curious if there may be other possible security issues.
Your user will be able to try calling any possible method from your class -- even try to call non-existant methods (and get a Fatal Error).
If you're fine with this... well, I suppose this is OK.
It doesn't look nice, but I don't think one could inject any other kind of code.
Still, I would at least check if the method exists -- using method_exists()
Yes its probably a bad idea, maybe you should restrict allowed methods. Maybe define allowed methods in an array then throw an exception if $method is not in this whitelist.
Also you will need to use the magic __call($name, $args) method to allow these user defined methods to be called.