Is it possible to create a mock object with disabled constructor and manually setted protected properties?
Here is an idiotic example:
class A {
protected $p;
public function __construct(){
$this->p = 1;
}
public function blah(){
if ($this->p == 2)
throw Exception();
}
}
class ATest extend bla_TestCase {
/**
#expectedException Exception
*/
public function testBlahShouldThrowExceptionBy2PValue(){
$mockA = $this->getMockBuilder('A')
->disableOriginalConstructor()
->getMock();
$mockA->p=2; //this won't work because p is protected, how to inject the p value?
$mockA->blah();
}
}
So I wanna inject the p value which is protected, so I can't. Should I define setter or IoC, or I can do this with phpunit?
You can make the property public by using Reflection, and then set the desired value:
$a = new A;
$reflection = new ReflectionClass($a);
$reflection_property = $reflection->getProperty('p');
$reflection_property->setAccessible(true);
$reflection_property->setValue($a, 2);
Anyway in your example you don't need to set p value for the Exception to be raised. You are using a mock for being able to take control over the object behaviour, without taking into account it's internals.
So, instead of setting p = 2 so an Exception is raised, you configure the mock to raise an Exception when the blah method is called:
$mockA = $this->getMockBuilder('A')
->disableOriginalConstructor()
->getMock();
$mockA->expects($this->any())
->method('blah')
->will($this->throwException(new Exception));
Last, it's strange that you're mocking the A class in the ATest. You usually mock the dependencies needed by the object you're testing.
Hope this helps.
Thought i'd leave a handy helper method that could be quickly copy and pasted here:
/**
* Sets a protected property on a given object via reflection
*
* #param $object - instance in which protected value is being modified
* #param $property - property on instance being modified
* #param $value - new value of the property being modified
*
* #return void
*/
public function setProtectedProperty($object, $property, $value)
{
$reflection = new ReflectionClass($object);
$reflection_property = $reflection->getProperty($property);
$reflection_property->setAccessible(true);
$reflection_property->setValue($object, $value);
}
Based on the accepted answer from #gontrollez, since we are using a mock builder we do not have the need to call new A; since we can use the class name instead.
$a = $this->getMockBuilder(A::class)
->disableOriginalConstructor()
->getMock();
$reflection = new ReflectionClass(A::class);
$reflection_property = $reflection->getProperty('p');
$reflection_property->setAccessible(true);
$reflection_property->setValue($a, 2);
Based on #rsahai91 answer above, created a new helper for making multiple methods accessible. Can be private or protected
/**
* Makes any properties (private/protected etc) accessible on a given object via reflection
*
* #param $object - instance in which properties are being modified
* #param array $properties - associative array ['propertyName' => 'propertyValue']
* #return void
* #throws ReflectionException
*/
public function setProperties($object, $properties)
{
$reflection = new ReflectionClass($object);
foreach ($properties as $name => $value) {
$reflection_property = $reflection->getProperty($name);
$reflection_property->setAccessible(true);
$reflection_property->setValue($object, $value);
}
}
Example use:
$mock = $this->createMock(MyClass::class);
$this->setProperties($mock, [
'propname1' => 'valueOfPrivateProp1',
'propname2' => 'valueOfPrivateProp2'
]);
It would be amazing if every codebase used DI and IoC, and never did stuff like this:
public function __construct(BlahClass $blah)
{
$this->protectedProperty = new FooClass($blah);
}
You can use a mock BlahClass in the constructor, sure, but then the constructor sets a protected property to something you CAN'T mock.
So you're probably thinking "Well refactor the constructor to take a FooClass instead of a BlahClass, then you don't have to instantiate the FooClass in the constructor, and you can put in a mock instead!" Well, you'd be right, if that didn't mean you would have to change every usage of the class in the entire codebase to give it a FooClass instead of a BlahClass.
Not every codebase is perfect, and sometimes you just need to get stuff done. And that means, yes, sometimes you need to break the "only test public APIs" rule.
You may want to use the ReflectionProperty as well.
$reflection = new ReflectionProperty(Foo::class, 'myProperty');
$reflection->setAccessible(true); // Do this if less than PHP8.1.
$reflection->setValue($yourMock, 'theValue');
Related
I am creating a class that uses ReflectionProperty and am getting strange results.
Essentially, calling $reflectionProperty->getType()->getName() is returning the value of some entirely unrelated array. It does this under seemingly random conditions.
See below:
// this will be used as a type hinted property.
class DummyClass {
public function __construct($arr) {}
}
// a class that sets its property values reflectively
class BaseClass {
/** #var ReflectionProperty[] */
private static $publicProps = [];
/**
* Gets public ReflectionProperties of the concrete class, and caches them
* so we do not need to perform reflection again for this concrete class.
*
* #return ReflectionProperty[]
* #throws ReflectionException
*/
private function getPublicProps(){
if (!static::$publicProps) {
$concreteClass = get_class($this);
static::$publicProps = (new ReflectionClass($concreteClass))
->getProperties(ReflectionProperty::IS_PUBLIC);
}
return static::$publicProps;
}
/**
* For each public property in this class set value to the corresponding value from $propArr.
*
* #param $propArr
* #throws ReflectionException
*/
public function __construct($propArr) {
$concreteClass = get_class($this);
echo "Creating new instance of $concreteClass<br>";
foreach ($this->getPublicProps() as $prop) {
// get which property to set, its class, and value to pass to constructor
$propName = $prop->getName();
$propClass = $prop->getType()->getName();
$propValue = $propArr[$propName];
$propValueStr = var_export($propValue, true);
// print out what we are about to do, and assert $propClass is correct.
echo "---Setting: ->$propName = new $propClass($propValueStr)<br>";
assert($propClass === "DummyClass", "$propClass !== DummyClass");
// create the instance and assign it
$refClass = new ReflectionClass($propClass);
$this->$propName = $refClass->newInstanceArgs([$propValue]);
}
}
}
// a concrete implementation of the above class, with only 1 type hinted property.
class ConcreteClass extends BaseClass {
public DummyClass $prop1;
}
// should create an instance of ConcreteClass
// with ->prop1 = new DummyClass(["foo"=>"abc123"])
$testArr1 = [
"prop1" => ["foo" => "abc123"]
];
// should create an instance of ConcreteClass
// with ->prop1 = new DummyClass(["boo"=>"abc123def456"])
$testArr2 = [
"prop1" => ["boo" => "abc123def456"]
];
$tc1 = new ConcreteClass($testArr1);
echo "Created TestClass1...<br><br>";
$tc2 = new ConcreteClass($testArr2);
echo "Created TestClass2...<br><br>";
die;
The results:
Creating new instance of ConcreteClass
Setting: ->prop1 = new DummyClass(array ( 'foo' => 'abc123', ))
Created TestClass1...
Creating new instance of ConcreteClass
Setting: ->prop1 = new abc123def456(array ( 'boo' => 'abc123def456', ))
Error: assert(): abc123def456 !== DummyClass failed
Notice that the value of $propClass is abc123def456 -- how did that happen?
More Weirdness
Change the value of "abc123def456" to "12345678" and it will work.
Change the value of "abc123def456" to "123456789" and it will not work.
Omit the var_export(), and it will work. (Though, it may still break in other cases).
My gut tells me this is a PHP bug, but I might be doing something wrong, and/or this may be documented somewhere. I would like some clarification, because as of right now my only reliable solution is to not cache the reflected $publicProps. This results in an unnecessary call to ReflectionClass->getProperties() every single time I create a new ConcreteClass, which I'd like to avoid.
Turns out this was a bug in PHP: https://bugs.php.net/bug.php?id=79820
Minimal reproduction here: https://3v4l.org/kchfm
Fixed in 7.4.9: https://www.php.net/ChangeLog-7.php#7.4.9
PHP 7 added support for anonymous classes, however I can't seem to find any information regarding associated scoping issues. I know I can use the use keyword with callables/closures to access outer scoped variables (like function() use ($outer) { // do work with $outer }), is there any way to do that with an anonymous class?
I would like to be able to do so without relying on the anonymous class constructor arguments, and without doing things like adding a setter method or public property to store the value after the instantiation.
Here's an example:
$outer = 'something';
$instance = new class {
public function testing() {
var_dump($outer); // would like this to dump the string 'something'
}
};
another solution could be
$outer = 'something';
$instance = new class($outer) {
private $outer;
public function __construct($outer) {
$this->outer = $outer
}
public function testing() {
var_dump($this->outer);
}
};
The unique way to access outside variable in this case is use $ _GLOBAL (I don't recommend). If you do not want to use constructor or setter method, my suggestion is to use a STATIC variable inside the anonymous class and set the value after the attribuition to the variable that contains the instance of anonymous class (Its not possible to define the static value before, because the class is anonymous..). Doing this, you have a better control and a static variable, but in certain way this is not very usual, every time when you create a new anonymous class the instance and it values belongs to the VARIABLE that receives the "new object", maybe is better for you to create a real class.. But follow a example with a static value and a anonymous class:
$i = new class {
public static $foo;
};
var_dump($i::$foo); //No value
$i::$foo = "Some value";
var_dump($i::$foo); //Has value
http://php.net/manual/en/language.variables.scope.php
There are some instructions in the php variable scope documentation.
This script will not produce any output because the echo statement refers to a local version of the $a variable, and it has not been assigned a value within this scope. You may notice that this is a little bit different from the C language in that global variables in C are automatically available to functions unless specifically overridden by a local definition. This can cause some problems in that people may inadvertently change a global variable. In PHP global variables must be declared global inside a function if they are going to be used in that function.
In php, the scope that a method inside a class can access is restricted to the inside of the entire class and cannot be accessed up to other scopes. So I think that the effect you want is not implemented in php, at least until the PHP GROUP decides to change the default behavior of PHP.
Of course, you can still use it by declaring variables as global.
Even though the OP did state that they would like to avoid public properties and anonymous class constructor arguments, the accepted answer is exactly that, so here is an example using a public property, which can be improved with a private property and a setter to maintain encapsulation:
class Foo {
public function executionMethod() {
return "Internal Logic";
}
}
$foo = new Foo();
var_dump("Foo's execution method returns: " . $foo->executionMethod());
$bar = new class extends Foo {
public $barVal;
public function executionMethod() {
return $this->barVal;
}
};
$bar->barVal = "External Logic";
var_dump("Bar's execution method returns: " . $bar->executionMethod());
I find this useful if you are unable to override the inherited class's constructor.
This will output:
string(46) "Foo's execution method returns: Internal Logic"
string(46) "Bar's execution method returns: External Logic"
If you want your anonymous class to have access to outer properties and methods that are protected or private, you could take advantage of the fact that closures inherit the scope they're defined in, and tie them into some magic methods for seamless behavior.
This is untested, but I'm pretty sure it'd work:
$class = new class {
/**
* #var \Closure
*/
public static $outerScopeCall;
/**
* #var \Closure
*/
public static $outerScopeGet;
/**
* #param string $name
* #param array $arguments
* #return mixed
*/
public function __call(string $name, array $arguments = [])
{
$closure = static::$outerScopeCall;
return $closure($name, $arguments);
}
/**
* #param string $name
* #param array $arguments
* #return mixed
*/
public function __get(string $name)
{
$closure = static::$outerScopeGet;
return $closure($name);
}
};
$class::$outerScopeCall = function (string $name, array $arguments = []) {
return $this->$name(...$arguments);
};
$class::$outerScopeGet = function (string $name) {
return $this->$name;
};
The $call closure will have access to the definition of $this from where it's defined as opposed to where
In TrainBoardingCard we have setTrainCode is it ok to use $this->trainCode = $trainCode inside constructor or we shoud always use setTrainCode like $this->setTrainCode($trainCode); as it could have some logic in future.
For both case what are the pros and cons? please let us know your preference with reasons.
class TrainBoardingCard extends BoardingCard
{
/**
* #var string
*/
private $trainCode;
/**
* #param string $trainCode
*/
public function __construct(string $trainCode)
{
$this->trainCode = $trainCode;
}
/**
* #return string
*/
public function getTrainCode(): string
{
return $this->trainCode;
}
/**
* #param string $trainCode
*/
public function setTrainCode(string $trainCode)
{
$this->trainCode = $trainCode;
}
}
It depends.
You could say there are two different schools of thought and they both deal with both setters and constructors.
The object must be created already valid state. This state can be changed with atomic operation from one valid state to another. This means, that you class doesn't actually have simple setters per se.
$client = new Client(
new FullName($name, $surname),
new Country($country);
new Address($city, street, $number));
// do something
$client->changeLocation(
new Country('UK'),
new Address('London', 'Downing St.', '10'));
Constructor is used only do pass in dependencies and not to impart state. The object's state by default is blank and it gets changed only by using setters externally.
$client new Client();
$client->setId(14);
$mapper = new DataMapper(new PDO('...'), $config);
$mapper->fetch($client);
if ($client->getCity() === 'Berlin') {
$client->setCity('London');
$mapper->store($client);
}
Or you can have a mix or both, but that would cause some confusion.
Not sure if this will make it better for you or worse :)
I have a class to manipulate objects with defined interface
class TaskManager
{
/**
* #param TaskInterface $task
* #param string $command
* #return TaskInterface
*/
public static function editTask($task, $command)
{
$task->setStatus(TaskInterface::TASK_STATUS_ACTIVE);
$task->setCommand($command);
$task->taskSave();
return $task;
}
}
I can create single object by passing its instance as an method argument. This is pretty straightforward. But how should I create many of them?
public static function export()
{
$commands = self::getCommandsToAdd();
foreach($commands as $c){
//This is wrong.
$task = new TaskInterface();
$task->setCommand($c);
$task->save();
//don't need to return it if it's saved
}
}
I can't create it this way. And it's obviously a bad idea to pass array of new objects. Another way is to pass a class name as a string and call its method to retrieve new object. But it seems wrong as well
I think I figured out a solution by myself. Can use a factory interface to pass a factory object.
interface TaskFactoryInterface
{
public static function createNew();
}
/**
* #param TaskFactoryInterface $task_factory
*/
public static function export($task_factory)
{
$commands = self::getCommandsToAdd();
foreach($commands as $c){
$task = $task_factory::createNew();
$task->setCommand($c);
$task->save();
//don't need to return it if it's saved
}
}
What do you think?
I am finding a lot of the time I pass an array of custom objects to a function for processing. In my function I check to make sure it is the right class before accessing internal properties/methods.
Because I am taking an array and as far as I am aware PHP doesn't have any typed generic list I cannot use type hinting so intellisense in my IDE doesn't work and code inspection throws warnings.
I came across an old post that gave the idea of throwing in this line of code in order to get intellisense working while developing:
if (false) $myObj = new MyObject();
So I end up with a function that looks like:
function ProcessObjectArray(array $arrayOfObject)
{
foreach ($arrayOfObject as $key => $myObj) {
if (get_class($myObj) == 'MyNamespace\MyObject') {
if (false) $myObj = new MyObject();
// I can now access intellisense for MyObject in here
} else {
trigger_error('is not right object');
}
}
}
It seems like a bit of a weird hack so I am wondering if this is a sign that I am not handling arrays of objects in the best way or if there is a better way to get my intellisense working. I had a look at the arrayobject interface to see if I could create a class implementing arrayobject that would hold a typed list. While it makes it nice to put all validation inside its constructor or append like functions I couldn't get it working with intellisense as the internal container is still just a standard array. Also using get_class doesn't seem good as if a class name or namespace is renamed then the IDE refactoring features do not pick this reference up (well, PHPStorm doesn't at least and it is one I am using).
I of course don't need intellisense but the weirdness of this hack made me wonder if I have the right approach to OOP in PHP and thought I might be missing something. Is this the right way of doing it or have I missed something?
with phpstorm when you add your annotations it will correctly do the code completion for you.
/**
* #param MyNamespace\MyObject[] $arrayOfObject
*/
function ProcessObjectArray(array $arrayOfObject)
{
foreach ($arrayOfObject as $key => $myObj) {
if (get_class($myObj) == 'MyNamespace\MyObject') {
if (false) $myObj = new MyObject();
// I can now access intellisense for MyObject in here
} else {
trigger_error('is not right object');
}
}
}
However intellisense won't stop you from passing in incorrect objects, it will only help you while coding to prevent passing in the incorrect objects.
Perhaps you may wish to consider using collections instead of using generic arrays.
There are lots of examples out there, however here is a simple one which you can expand upon
class Collection
{
/** #var array */
private $items = [];
/**
* #param MyNamespace\MyObject $obj
* #return $this
*/
public function addItem(MyNamespace\MyObject $obj) {
$this->items[] = $obj;
return $this;
}
/**
* #param $index
* #return $this
*/
public function deleteItem($index) {
unset($this->items[$index]);
return $this;
}
/**
* #param $index
* #return MyNamespace\MyObject|null
*/
public function getItem($index) {
return array_key_exists($index, $this->items) ? $this->items[$index] : null;
}
/**
* #return MyNamespace\MyObject[]
*/
public function getItems() {
return $this->items;
}
/**
* #return int
*/
public function count() {
return sizeOf($this->items);
}
}
here is an example how it all works:
$collection = new \Collection;
$obj = new MyNamespace\MyObject;
$collection->addItem($obj);
foreach($collection->getItems() as $item) {
// this will already know that $item is MyNamespace\MyObject because of the annotation on the getItems() method
$item->doSomething();
}
class MyCollection implements \Iterator
{
/**
* #var CollectionElement[]
*/
private $elements = [];
public function addElement(CollectionElement $element)
{
$this->elements[] = $element;
}
/**
* #return CollectionElement
*/
public function current()
{
return current($this->elements);
}
//... Rest of the Iterator implementation
}
This way you cannot add to the collection anything other than CollectionElement. And after:
foreach($collection as $element){
/** #var CollectionElement $element */
$element->//here you will have autocompletion
}
You don't need to check anything since your collection will never contain anything you are not expecting.
If you are going this route, I would avoid using the are native arrays. I would make classes that contain arrays of a certain type. That way, you can validate the array contents in the constructor of the class, and then you can use actual type hinting for the objects, which PHP allows.