PHPDoc: #return void necessary? - php

Is it really necessary do something like this:
/**
* ...
*
* #return void
*/
I have quite a few methods that don't have a return value, and it seems really redundant to put something like this in the comment. Would it be considered bad form to leave it out?

If it makes it clear for the documentation, then leave it in, but it isn't strictly necessary. It's an entirely subjective decision.
Personally, I would leave it out.
EDIT
I stand corrected. After a little googling, the wikipedia page says:
#return [type description] This tag should not be used for constructors or methods defined with a void return type.
The phpdoc.org website says:
#return datatype description
#return datatype1|datatype2 description
The #return tag is used to document the return value of functions or methods. #returns is an alias for #return to support tag formats of other automatic documentors
The datatype should be a valid PHP type (int, string, bool, etc), a class name for the type of object returned, or simply "mixed". If you want to explicitly show multiple possible return types, list them pipe-delimited without spaces (e.g. "#return int|string"). If a class name is used as the datatype in the #return tag, phpDocumentor will automatically create a link to that class's documentation. In addition, if a function returns multiple possible values, separate them using the | character, and phpDocumentor will parse out any class names in the return value. phpDocumentor will display the optional description unmodified.
Sooo... Based on that, I would say leave out the void. It's non-standard, at least.

According to phpDocumentor, #return void is valid:
http://www.phpdoc.org/docs/latest/guides/types.html#keywords
...
this type is commonly only used when defining the return type of
a method or function. The basic definition is that the element
indicated with this type does not contain a value and the user should
not rely on any retrieved value.
For example:
/**
* #return void
*/
function outputHello()
{
echo 'Hello world';
}
In the example above no return statement is specified and thus is the
return value not determined.
Source: http://www.phpdoc.org/docs/latest/for-users/phpdoc/types.html (archived page).

I have to edit my answer because of something I have learned recently.
Using #return void instead of #return null has a very special meaning, consider the following two examples of PHP code.
<?php
/**
* #return void
*/
function return_never() {
echo "foo";
}
/**
* #return null|string
*/
function return_sometimes() {
if ($this->condition()) {
return "foo";
}
}
In the first example PHP will actually return NULL, since PHP always returns NULL. But the returned value is of no use to the caller since it does not say anything about what the function did. IDEs can use the documented information of #return void to indicate the developer that a return values is used which serves no purpose.
<?php
$foo1 = return_never();
$foo2 = return_sometimes();
The first call is senseless since the variable will always contain NULL, the second one might actually contain something. This is becoming even more interesting if we put the function calls into a conditional.
<?php
if (($foo1 = return_never())) {
// Dead code
var_dump($foo1);
}
if (($foo2 = return_sometimes())) {
var_dump($foo2);
}
As you can see, #return void has its use cases and should be used if applicable.
Also note that it is going to be a part of the upcoming PHP PSR-5 standard.[1]
[1] http://www.php-fig.org/psr/

As of php 7.1, void is a valid return type and can be enforced on a function.
I would always add it on the docblock.
Another benefit of writing it, is to differentiate the void methods from the methods that may return anything but don't have a #return entry on the docblock by negligence.

Here is how I understand and use PhpDocumentor annotations:
<?php
/**
* This method always returns string.
* #return string
*/
public function useCase1()
{
return 'foo';
}
/**
* This method returns 2 data types so list them both using pipeline separator.
* #return string|false
*/
public function useCase2()
{
if ($this->foo === 1) {
return 'foo';
}
return false;
}
/**
* This method performs some operation and does not return anything so no return
* annotation is needed.
*/
public function useCase3()
{
$this->doOperation();
$this->doAnotherOperation();
}
/**
* If condition passes method returns void. If condition does not pass it returns
* nothing so I think that specifying the return annotation with void is in space. :)
* #return void
*/
public function useCase4()
{
if ($this->foo === 1) {
$this->doOperation();
return;
}
$this->doAnotherOperation();
}

Personally, I think the big thing missing from this is that documenting a function returns at all is important. Currently standards dont have any documentation for functions that never return....hence a return void is way of saying yes this function does actually return.
Consider this code block
<?php
/**
* #return void
*/
function return_void() {
echo "foo";
}
/**
* #return null|string
*/
function return_sometimes() {
if ($this->condition()) {
return "foo";
}
}
/**
* This function actually doesnt return at all - it kills the script
**/
function noreturn() {
//do somthing then
die(); //or exit()
}
Clearly the use of #return at least indicates the function does return

Related

PhpStorm Generic Classes not working at all

This is all happening for me in PhpStorm 2022.2.1
I've been trying to set up a class in PHP that supports Generics, but to no avail. No matter what I tried, PhpStorm always type hints as mixed. Here is a pair of classes as an example:
/** #template T */
class MyGenericClass
{
/** #var T */
public $param;
/** #param T $param */
public function __construct($param)
{
$this->param = $param;
}
/** #return T */
public function getParam()
{
return $this->param;
}
}
class Test
{
public static function test(): void
{
$generic = new MyGenericClass(1);
$a = $generic->getParam();
$b = $generic->param;
}
}
I would expect $generic->getParams() and $generic->param to be hint typed as int, but both of these show up as mixed. I also tried adding a #var MyGenericClass<int> $generic, but it does not help, and PhpStorm warns that it's already inferred from source code.
I also tried to set this up with arrays, and I even tried using Symfony ArrayCollections that are supposed to work - passing an array of integers and calling first() on a Symfony ArrayCollection is hint typed as mixed too.
On the other hand, function scoped templates work just fine. If I make a function like
/**
* #template T
* #param class-string<T> $param
* #return T
*/
public static function test2(string $param)
{
return new $param();
}
the return type is hinted as mixed|T, and when passing an actual class string, it does correctly hint for result functions, although that seems to also be the case even if I get rid of templates. In a similar case, I had hint show up as the actual passed type, not sure why it's showing up as T instead of the Class string passed to it?
I'm not sure whether I'm missing something, or maybe something is messed up with my configuration?

PhpStorm: what to do with unhandled exceptions when preconditions are met

I really like the PhpStorm inspection tools. They help me a lot to write better code. Now I have the following situation and I am asking myself what's the best way to deal with such situations.
I have a function f with some precondition, for example like in the following code:
/**
* #param int $x
* #throws PreconditionException x is negative
*/
public function f(int $x): int
{
if ($x < 0) {
throw new PreconditionException("the input x is negative");
}
}
Then I use this function somewhere lets say the following:
f(5);
Now PhpStorm warns me with "Unhandled exception". But in this case I know that the exception will not be thrown, so adding a try block makes not really sense. Should I simply ignore this warning or what's the best way to deal with that?
From phpStorm version 2018.1 you can exclude any exceptions from analysis. Go to preferences->Languages & Frameworks->PHP and open Analysis tab.
Here you can add exceptions to Unchecked Exceptions list
#noinspection tag can be used to instruct PhpStorm to suppress an inspection.
The tag can be used above the line, above the method or on top of the file after <?php word:
/** #noinspection PhpDocMissingThrowsInspection */
/**
*
* Cancels order.
*
* #return bool
*/
public static function cancel()
{
if (!self::inProgress()) return false;
/** #noinspection PhpUnhandledExceptionInspection*/
static::current()->delete();
return true;
}
List of inspections can be found in this gist: https://gist.github.com/discordier/ed4b9cba14652e7212f5
You can also disable it via interface. ALT+ENTER then right arrow and Suppress ...
The correct way is add #throws tag into documentation (PhpStorm manual).
For example:
/**
* #param $userId
* #return array|null
* #throws \Exception <---------------------------
*/
public static function send($userId)
{
if (empty($userId)) {
throw new \Exception("User ID is missing", 1);
}
// ...
}
What helps for me is to set #throws to Null
Example:
/**
* #return SomeObject
* #throws Null
*
*/

What's the most elegant way to have functions return non-binary states?

Is there a generally agreed upon solution in PHP for creating functions that return non-binary states? For example, if a function is going to return "success" or "failure", it's simple to just spit back a boolean but what if it needs to return failure vs. thing_1_happened vs. thing_2_happened?
In the past, I've had functions return ints or strings that match constants, like
$result = $myClass->whatever();
if ($result === MyClass::Thing1Happened) {
...
} elseif ($result === MyClass::Thing2Happened) {
...
}
But it just feels inelegant and classes end up with tons of constants only used by single functions. I imagine it's better than returning magic, unnamed values, which would lead to typo bugs (if you used strings) or reduced readability (if you used ints) but is there a better way?
If there's a correct term for "non-binary state returns" that would make this Google-able, please enlighten me.
I would recommend creating a new value object class to represent the state. The class would probably just need one member variable, state and the possible state values could be defined with constants. Having an object like this allows you to write more complex logic against your states. This also keeps the state representation logic contained in a single class which is great for testing, preventing defects, etc.
class EventStateValueObject
{
/**
* Side note: this has to be declared as protected; if declared as public, the magic getters and setters are bypassed
* #var int
*/
protected $state;
/**
* #param string $name
* #param mixed $value
* #return void
*/
public function __set($name, $value)
{
throw new RuntimeException('Value objects are immutable');
}
/**
* #param string $name
* #return mixed
*/
public function __get($name)
{
return $this->$name;
}
/**
* #param int $state
*/
public function __construct(int $state)
{
$this->state = $state;
}
}

PHPMD says violating Single Responsibility Principle for argument having boolean default value

These two PHP class methods violates Single Responsibility Principle (SRP) according to the phpmd rule booleanargumentflag.
How should they be written to avoid this?
If the solution is to remove the default value "= true", then how is this improving the code?
/**
* Set verbose mode.
*
* #param boolean $mode true or false to enable and disable verbose mode,
* default is true.
*
* #return $this
*/
public function setVerbose($mode = true)
{
$this->verbose = $mode;
return $this;
}
/**
* Use cache or not.
*
* #param string $use true or false to use cache.
*
* #return $this
*/
public function useCache($use = true)
{
$this->useCache = $use;
return $this;
}
The intention of this hint is to reduce the method's responsibilities.
In this particular case, if the function of the method is to set some kind of behavior, it shouldn't have any default value. The default values belong to the class definition or its constructor.
You can just remove the parameter's default values and set them when you define the class properties. I.E.:
public $useCache = true;
Your example has two methods, each with two responsibilities: turning the thing on, and turning it off. Better to split each method to give each one responsibility only. Example:
public function setVerbose($flag);
becomes
public function setVerboseOn();
public function setVerboseOff();

In php, how can I determine equality when an object proxy is involved?

In my php application I have been comparing objects with the usual equality comparison operator, e.g.:
if ($objectA == $objectB) { ... }
Recently I implemented proxies (for objects which are expensive to load) however this means the equality operator no longer works. Is there a simple way around this? One that doesn't rely on reflection?
For the moment, I have resorted to testing the unique identifier of each object, e.g.
if ($objectA->getId() == $objectB->getId) { ... }
But this has two problems: 1) I need to refactor all existing code, and 2) in the future I may need to compare objects which are value objects (not entities).
I'm not hopeful of an easy solution since I think it would require a new magic method...
Here's my AbstractProxy class. Any help appreciated...
abstract class KOOP_Base_AbstractProxy
implements KOOP_Base_iDomain
{
use KOOP_Trait_Helper_Helper;
/**
* #var integer Object identifier
*/
protected $_id = null;
/**
* #var KOOP_Base_AbstractMapper
*/
protected $_mapper = null;
/**
* #var KOOP_Base_AbstractDomain Actual object
*/
protected $_subject = null;
/**
* Store object id for lazy loading
*
* #param integer $id Object identifier
* #param string $mapper Mapper by which to retrieve object
*/
public function __construct($id, $mapper)
{
$this->_id = $id;
$this->_mapper = $mapper;
}
/**
* Get subject
*
* #return KOOP_Base_AbstractDomain
*/
protected function getSubject()
{
if (!$this->_subject) {
$this->_subject = $this->getMapper($this->_mapper)->find($this->_id);
}
return $this->_subject;
}
/**
* Get property
*
* #param string $property
* #return mixed
*/
public function __get($property)
{
return $this->getSubject()->$property;
}
/**
* Set property
*
* #param string $property
* #param mixed $value
* #return void
*/
public function __set($property, $value)
{
$this->getSubject()->$property = $value;
}
/**
* Is property set?
*
* #param $property
* #return boolean
*/
public function __isset($property)
{
return isset($this->getSubject()->$property);
}
/**
* Unset property
*
* #param string $property
* #return mixed
*/
public function __unset($property)
{
unset($this->getSubject()->$property);
}
/**
* Call method
*
* #param string $method Method to call
* #param array $params Parameters to pass
* #return mixed
*/
public function __call($method, array $params)
{
return call_user_func_array(array($this->getSubject(), $method), $params);
}
/**
* Get id
*
* Saves having to retrieve the entire object when only the ID is required.
*/
public function getId()
{
return $this->_id;
}
}
Proxies do break object equality, and there's no utterly clean way to fix this. In a fully object oriented language you would handle this by operator overloading (which I don't recommend) or implementing a custom .equals() function (as in Java). Sadly, PHP simply does not support object orientation at this level, so you will have some decisions to make.
1) I would prefer to have your proxy class provide an equals() function which takes as input a reference to the object you want to test against and compares it to the proxied object - which shouldn't be much more 'expensive' than it was to not use a proxy at all. Example in pseudo-PHP code (my apologies if my reference syntax is off, it's been a while):
public function equals (&$toCompare)
{
if ($_subject == $toCompare)
{
return true;
}
else
{
return false;
}
}
The downside is simple: you have to refactor your code that involves this proxied object, and you have to remember that "==" does not work on this proxied object type while you are working. If you don't deal with these objects much, or if you deal with them all the time, this is fine. If you deal with them regularly but intermittently, or if others must work with them on occasion, then this will cause bugs when you/they forget about this equality problem.
2) Use an Operator Overloading extension to the language. I haven't done this, I don't know if it works, and it might be a nightmare. I include it for theoretical completeness.
Personally, I think I'd just hack it with the pseudo-Java approach call it a day, as I think it would actually work and require nothing more than using the function correctly (and remembering to use it in the first place).

Categories