How can I use docblock hints with classes for $this variables? - php

I'm using NetBeans as my IDE. Whenever I have some code that uses another function (usually a factory) to return an object, typically I can do the following to help with hinting:
/* #var $object FooClass */
$object = $someFunction->get('BarContext.FooClass');
$object-> // now will produce property and function hints for FooClass.
However, when I use an object's property to store that class, I'm at a bit of a loss how to do the same, as trying to use #var $this->foo or #var foo will not carry the hinting through:
use Path\To\FooClass;
class Bar
{
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo //does not have hinting in IDE
}
}
I have tried in the docblock for the class, or using the inline comments above protected $foo or where foo is set to the instance.
The only workaround I have found so far is to:
public function bat()
{
$this->foo = FactoryClass::get('Foo');
/* #var $extraVariable FooClass */
$extraVariable = $this->foo;
$extraVariable-> // now has hinting.
}
I would really like to have the hinting be class-wide though, as many other functions could potentially use $this->foo, and knowing the class's methods and properties would be useful.
Surely there is a more straightforward way...

I cannot say how it works in Netbeans, but in PHPEclipse, you would add the hint to the declaration of the variable itself:
use Path\To\FooClass;
class Bar
{
/**
* #var FooClass
*/
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo // should now have hinting
}
}

Given
class Bar
{
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo //does not have hinting in IDE
}
}
The IDE is trying to get the declaraion from FactoryClass::get which probably has no docblock return type. The problem is if this factory method can return any number of classes, there isn't much you can do except for using your workaround.
Otherwise, it wont know the difference between FactoryClass::get('Foo') or FactoryClass::get('Bar') since those two calls most likely would return objects of different types.

Related

PHP type hinting from object to my class, not possible?

I've setup an abstract class that must execute methods from another instanciated class.
So I setup an anonymous class that extends my abstract class and in the implemented method, I want to access the methods of that "other instanciated class", but for my IDE to help me to find the correct name of the methods and arguments, I must write somewhere the type.
abstract class blah
{
protected object $reference;
public function __construct(object $reference)
{
$this->reference = $reference;
$this->hello();
}
abstract public function hello();
}
class world
{
public function __construct()
{
new class($this) extends blah {
public function hello()
{
$this->reference->display();
}
};
}
public function display()
{
echo 'hello world';
}
}
new world();
I can't change the type from "object" to something else to allow my IDE to show the methods
public function __construct()
{
new class($this) extends blah {
protected world $reference;
public function hello()
{
$this->reference->display();
}
};
}
this throws
Fatal error: Type of blah#anonymous::$reference must be object (as in class blah)
I could copy the attribute "reference" to another variable and set a typehint to "world", but that's not the best
public function __construct()
{
new class($this) extends blah {
protected world $reference;
public function hello()
{
/** #var world $hello */
$hello = $this->reference;
$hello->display();
}
};
}
what better solution do I have ?
edit: this seems to work, is it the best way ?
new class($this) extends blah {
/** #var world $reference */
protected object $reference;
public function hello()
{
$this->reference->display();
}
};
It's worth understanding why PHP is telling you that you can't change the property type.
Since this is a protected property, it can be read and written by either the parent or child class. For instance, the parent class might have methods like this, which would be inherited by the child class:
public function getReference(): object {
return $this->reference;
}
public function setReference(object $newReference): void {
$this->reference = $newReference;
}
public function setReferenceToDefault(): void {
$this->reference = new SomethingBoring;
}
If you specify world as the type of $reference in a child class, the getReference method will work fine - any instance of world is an object, so the return type is OK. But the setReference() and setReferenceToDefault() methods would fail, because they try to assign something that isn't of type world.
Essentially, by declaring a protected property on the base class, you are setting a contract for all child classes, and PHP is enforcing it for you. The technical term for this is "contravariance": a child class can be more generous in what it accepts, but it can't reject values the parent would accept. The opposite is "covariance", which applies to returning values: the child class can be more specific about what it will return, but can't return something the parent class never would. When something is both input and output, you get both restrictions, so can't vary the type at all.
Since you don't actually use the property on the base class, this restriction isn't actually needed, so in this case one option is simply not to define the property or constructor on the base class. In other cases, you might want to use traits, which are "compiler assisted copy-and-paste": they don't assert any relationship between two classes, but they save you writing the same code more than once.
If you want it only for IDE, then use documentation block to overwrite type declaration (if IDE supports it):
/**
* #property world $reference
*/
new class($this) extends blah {
public function hello()
{
$this->reference->display();
}
};
E.g. PHPStorm, while not supporting that for anonymous class, supports for normal class description:

PHP5: Objects reference

I'm writing some entities class in php, that may point between each other with a repository class (to avoid to query too much the database using a local entity repository):
class Foo
{
public $fooId;
public $bar;
function __construct($entity_id)
{
/**
* Some Database stuff to get Foo object based on $entity_id
**/
$this->bar = Repository::get('Bar', $databaseStuff->barId);
}
}
class Bar
{
public $foo;
function __construct($entity_id)
{
/**
* Some Database stuff to get Bar object based on $entity_id
**/
$this->bar = Repository::get('Bar', $databaseStuff->barId);
}
}
class Repository
{
private static $entities = [];
/**
* #param string $entity_name
* #param int $entity_id
* #return mixed
*/
public static function &get($entity_name, $entity_id)
{
$entity_name = ucfirst(strtolower($entity_name));
if(!isset(self::$entities[$entity_name][$entity_id]))
{
self::$entities[$entity_name][$entity_id] =& $entity_name($entity_id);
}
return self::$entities[$entity_name][$entity_id];
}
}
$foo = new Foo(1337);
The problem is that i get a sort of timeout/stack overflow probably due to the fact of $foo->bar is a reference to a Bar object, but $bar->foo is a reference to a Foo object, etc...
I did not forgot to declare my function to return a reference using &get(), am i right?
My class is instantiated with =& operator.
What could be wrong in my code (or in my logic) ?
Thanks.
If I see it right, the constructor of Bar tries to create new Bar.
I suppose such a nested constructing will never end. Its the same e.g. when a function calls itself recursively with no way to exit.
The script will even not come to writing into Repository.
Ever tried xdebug? That could show the problem quite quickly.
And btw there are possibli bad Copy/Paste. The comment in class Foo does not match the line fallowing it. And you are setting $this->bar in class Bar, but only class variable $foo is declared.

PHP: How to provide non-primitive default values for properties in traits?

Is there any way to provide default objects for properties in traits?
trait myTrait{
private $foo = 0; // works
private $bar = new stdClass(); // doesn't work.
}
I know it might seems as a bad coding style to instantiate an concrete object as default value (due to high coupling). The idea here is to provide a NullObject for an optional depency.
Example use-case
A better example (the one I'm currently thinking about):
Use a Trait for Logging and implement the default NullLogger of the PSR-3 Logging as default attribute:
trait LoggerTrait{
/**
* #var Psr\Log\LoggerInterface
*/
protected $logger;
public function setLogger(Psr\Log\LoggerInterface $logger){
$this->logger = $logger;
}
}
class Foo{
use LoggerTrait;
public function __construct(){
$this->setLogger(new Psr\Log\NullLogger()); // I would like to avoid this line as I'd need to duplicate it in every class I'm using the LoggerTrait.
}
public function doStuff(){
$this->logger->info("Yey flexible logging with no overhead!");
}
}
This works, but I have to set the NullLogger explicitly in every class that uses the trait and I would like to avoid this "duplication of code".
PS: I'm sure some one will bring up that the logger could also be inserted via DI-container. That is true but imho not really necessary. See this blog post + comments for some pro/contra arguments :)
A common way of solving this problem is using a getter within the class, even though the class owns the property.
trait LoggerTrait{
/**
* #var Psr\Log\LoggerInterface
*/
protected $logger;
public function setLogger(Psr\Log\LoggerInterface $logger){
$this->logger = $logger;
}
public function getLogger()
{
if(null === $this->logger) {
$this->logger = new Psr\Log\NullLogger();
}
return $this->logger;
}
}
This way the default object isn't even instantiated until it is needed.
class Foo{
use LoggerTrait;
public function doStuff(){
$this->getLogger()->info("Yey flexible logging with no overhead!");
}
}

way to specify class's type of an object in PHP

Is there a way to specify an object's attribute's type in PHP ?
for example, I'd have something like :
class foo{
public bar $megacool;//this is a 'bar' object
public bar2 $megasupercool;//this is a 'bar2' object
}
class bar{...}
class bar2{...}
If not, do you know if it will be possible in one of PHP's future version, one day ?
In addition to the TypeHinting already mentioned, you can document the property, e.g.
class FileFinder
{
/**
* The Query to run against the FileSystem
* #var \FileFinder\FileQuery;
*/
protected $_query;
/**
* Contains the result of the FileQuery
* #var Array
*/
protected $_result;
// ... more code
The #var annotation would help some IDEs in providing Code Assistance.
What you are looking for is called Type Hinting and is partly available since PHP 5 / 5.1 in function declarations, but not the way you want to use it in a class definition.
This works:
<?php
class MyClass
{
public function test(OtherClass $otherclass) {
echo $otherclass->var;
}
but this doesn't:
class MyClass
{
public OtherClass $otherclass;
I don't think this is planned for the future, at least I'm not aware of it being planned for PHP 6.
you could, however, enforce your own type checking rules using getter and setter functions in your object. It's not going to be as elegeant as OtherClass $otherclass, though.
PHP Manual on Type Hinting
No. You can use type hinting for function parameters, but you can not declare the type of a variable or class attribute.
You can have other class objects in your current class, but you must make it in __contractor (or somewhere else) before use.
class Foo {
private Bar $f1;
public Bar2 $f2;
public function __construct() {
$f1 = new Bar();
$f2 = new Bar2();
}}
class Bar1 {...}
class Bar2 {...}
You can specify the object-type, while injecting the object into the var via type-hint in the parameter of a setter-method. Like this:
class foo
{
public bar $megacol;
public bar2 $megasupercol;
function setMegacol(bar $megacol) // Here you make sure, that this must be an object of type "bar"
{
$this->megacol = $megacol;
}
function setMegacol(bar2 $megasupercol) // Here you make sure, that this must be an object of type "bar2"
{
$this->megasupercol = $megasupercol;
}
}

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