What is the most useful/most standard/least surprising way to consistently write comment blocks for constructors and classes and files containing just a single class?
Comment blocks for classes, not constructors
Comment blocks for constructors, not classes
Comment blocks for both constructors and classes -> In that case what sort of details should go in each?
And then the file itself? Does this need a comment block if it just contains a single class? What details should go there?
I want to try and avoid, as much as possible, repetition between class, constructor and file comment blocks.
This is highly depending on your context and the assumed skill level of the people you work with or the people that come after you.
If you publish a framework, a library or something of the sort you usually assume that your user are of all skill levels so you might want to document as much trivial crap as possible to reduce the load of questions your community has to handle.
Let me start of with an example where I think documentation can be a major pain.
What do you absolutely need
If you want to use PHPDoc you need a file doc block and another doc block after that (usually the class doc block).
Those **need* to have a #package Tag. Everything else is optional.
I'd go so far and say that even the #package Tag is optional since you might be able to automatically generate it for project. And if i remember correctly PHPDoc lets you even set a default package for everything that doesn't have a tag.
For documentation in general let me start of with an example (a longer example is at the end):
How many times can you explain what "uri" means:
Note that for getUri it is explained what URI stands for (just to have something to talk about in the comment I'd assume) while it isn't in isAbsUri because there you can at least say "abs means absolute" twice.
If you are not an open source project (or need to ship COMPLETE!!!11eleven api documentation):
I'd strongly suggest to use proper, long and descriptive class, variable and method names instead of documentation.
There is no gain in writing something again in the doc blocks and since it is 2011 and we have 120 char wide terminals and autocompletion there is just no need anymore to abbreviate everything for the sake of saving some chars.
I'd even argue that documenting trivial things hurts you and your team by forcing you to waste time on things nobody gets value from you get into a habit of always writing trivial docs and not reading them anymore.
A good comment should explain WHY something was done while the code its self should explain HOW without needing further comments.
My favorite example for redundant docs is this one:
class myClass {
/**
* Constructor
*/
public function __construct() {
}
Some guidelines say you HAVE to document EVERYTHING and you end up with people stating the obvious again and again.
This adds no value but wastes time when reading the code.
An example for proper naming:
class Person {
/**
* Set a persons weight in Kilogram
*
* #param float $kg Weight in Kilogram
*/
public function setWeight($kg) {}
This code is easy to document because you need to explain what "kg" means because some people might use a different system and you can't google for "kg".
I'm in favor of writing
class Person {
/**
* #param float $kilogram
*/
public function setWeight($kilogram) {}
The doc block is superfluous because calling setWeight on Person can REALLY be expected to set the Weight on a Person. No need to write that out again.
Using $kilogramm as a parameter also saves you the trouble of explaining it in the docs and I'd say, depending on your environment, everyone can be expected to google for "kilogram" if he really doesn't know the measurement unit.
#PHPDoc documentation
All my humble opinion of course
If you don't use type-hinting always use #param tags.
Always use #return tags
Don't use #author tags at all. Collection code ownership is more valuable and the information is in the source control repository anyways.
Only use #copyright tags if you have to. I like only having a LICENSE file but I'm not a lawyer so it might be necessary.
Inline comments:
public function generateReport() {
// get the db connection
$reg = WhateverGlobalStorage::get(“db“);
// auth
if(!$reg->getOne("SELECT view_report FROM USER ...")) {}
// template
$id = $reg->getOne("select ... ");
// render
new ReportTemplate($id); // ...
}
If those are separate "blocks" just move them to descriptive named functions
public function generateReport() {
$this->checkAuthentication();
$template = this->createReportTemplate();
$this->renderReport($template);
}
// Not perfect but at least you can grasp what the method does much quicker
Additional resources:
Slides of a presentation I gave on the subject on some conferences: Slideshare: clean-code-stop-wasting-my-time
And additional small, little older, rant: they-told-you-to-document-everything-they-lied
Book references:
Clean Code: A Handbook of Agile Software Craftsmanship
Refactoring: Improving the Design of Existing Code
A longer example
abstract class xyzRequest {
/**
* Initializes this xyzRequest.
*
* Available options:
*
* * logging: Whether to enable logging or not (false by default)
*
* #param xyzEventDispatcher $dispatcher An xyzEventDispatcher instance
* #param array $parameters An associative array of initialization parameters
* #param array $attributes An associative array of initialization attributes
* #param array $options An associative array of options
*
* #return bool true, if initialization completes successfully, otherwise false
*
* #throws <b>xyzInitializationException</b> If an error occurs while initializing this xyzRequest
*/
public function initialize(xyzEventDispatcher $dispatcher, $parameters = array(), $attributes = array(), $options = array()) {
Lets see, line by line, what that documentation tells you.
(I'm joking around here a little bit to get my point across)
* Initializes this xyzRequest.
So calling ->initialize on a xyzRequest initializes that request? Really? Ok then, if you say so!
* Available options:
*
* * logging: Whether to enable logging or not (false by default)
We are told the options for the third parameter, not for the second or third param but maybe we know those if we know the framework? (Since we are not able to figure out what ->initialize does without someone telling use we might not be that smart...)
* #param xyzEventDispatcher $dispatcher An xyzEventDispatcher instance
Yeah, the type-hint is there. So if the method expects a "xyzEventDispatcher instance" we need to pass in a "xyzEventDispatcher instance". Good to know.
* #param array $parameters An associative array of initialization parameters
* #param array $attributes An associative array of initialization attributes
* #param array $options An associative array of options
Ok. So it's not a linear array. But that I need to pass "initialization parameters" to an "initialize" method I could have figured out.
Still no clue what I actually need to pass in there but as long as its documented it has to be fine!
* #return bool true, if initialization completes successfully, otherwise false
So a boolean return value is "true" for "good" and "false" for bad".
* #throws <b>xyzInitializationException</b> If an error occurs while initializing this xyzRequest
*/
So an exception is thrown if an error occurs while we are doing what the function is named?
So exceptions are used for error cases. Ok. Good to know.
Doesn't tell me the difference between return false and an exception.
The #throws its self is fine because it adds information
Btw: Why is this BOLD and not a #link
Personally, I only comment in the constructors if there is something special to comment about it (like a special initialization).
I wouldn't say that's the "most useful" way to go, but it keeps the code neat, and repeating two times the same thing isn't really needed (if that's your concern).
Comment everything - files (authorship, copyright, description etc), classes (description, code examples), methods and properties. Here is a good example with phpDoc comments.
Personally I think that class and method documentation is the most important documentation around. When I write code I want the help of my IDE when the code completion shows me the documentation belonging to a method. This way I can easily find the method I need.
Since I try to keep the explicit initialization of classes to a minimum, I don't user constructor comments. Hence, I try to avoid the use of constructors itself.
The code in a method or function should be as clear as possible by using declarative variable names and keeping them so small as possible. Only when I do something unexpected, by example for integration issues I comment them.
Related
I'm currently building up some phpdoc for a restful API - and I took to using the #param doc syntax for notating required params over POST.
However, after generating the phpdoc, I noticed that it refuses to list these parameters unless they match exactly with input variables into the method itself.
#uses and #see don't look good nor make much sense here when it comes to the phpdoc output. The style/look of the doc is perfect with the #param functionality.
Is there any way to override the rules put in place by PHPDoc, and allow it to generate #param blocks in the documentation, even if the param doesn't exist in the method itself?
If you want to document your API, I suggest you use proper tools like API Blueprint or Open API Spec.
And by using Swagger, you can even use annotations (which is what you apparently want) to document the API and in turn, generate the documentation out of it.
Just don't use PHPdoc for it, because that's just mixing concepts altogether.
Alright, I'm going to answer this with the solution I've found - I appreciate all the "do not do that" answers, but still hope that someone who finds themselves in a similar situation to myself ala "this needs to be done immediately without changing the format, and we cannot allot any time to this" will find it useful in the future.
You can maintain using the #param syntax if you initialise the method with the param specified, and simply set it to null - ensuring it doesn't break any existing calls.
/**
* Remove a group
*
* #param int $pricing_group_id Required
* #return mixed JSON array with remaining groups
*/
public function remove($pricing_group = null) {
....
}
Your PHPDoc output will now show the param type, name, and description as normal.
Keep in mind that this is not good practice - nor is it even remotely correct practice. But it'll work until you can convince a higher-up to allot you enough time to rebuild the existing documentation on a more suitable platform.
You can do this by utilizing "optional". IE:
#param string $variable (optional) Blah.
See other examples at https://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.param.pkg.html .
This is to be used in the use case of when a parameter is optional, unlimited parameters, etc.
You might consider using a "custom" tag, maybe #parameter, so that it is listed "as is". You won't get the same hyperlink behavior for class names as the param tag provides, but you won't be limited by the lack of parameters in the code signature itself. Maybe use an inline link tag in the parameter description that points to the class type of the parameter. Otherwise, the uses, see, or link regular tags can be on separate lines as convenient links to the classes that are your parameter types.
There is currently no way to disable the internal behavior of "actual code signature overrides param tags" logic.
I believe you can use the same approach for documenting virtual methods as a workaround, i.e. via a #method entry in the phpDoc header for the class.
E.g.:
/**
* ...
*
* #method mixed remove(integer $pricing_group_id) Remove a group and return a JSON array with remaining groups.
*/
class YourClass {
...
// see class header for phpdoc entry
public function remove($pricing_group = null) {
....
}
}
A downside is that (to my knowledge) this approach does not provide a means to enter explicit description entries for the method's parameters and return value.
I'm using eclipse to build my php applications and the Source > Generate Element Comment function for automatically creating doc blocks.
I'm wondering a bit about the format because the return type is set as full-qualified class name but the parameters not.
Is this a bug of the IDE or is it the common convention?
Here's an example:
/**
* Executes the given service.
*
* #param string $serviceClass
* #param ParametersInterface $inputParams
*
* #return \ZF\Hal\Entity
*/
And I have the following uses:
namespace MyApp;
use ZF\Hal\Entity;
use Zend\Stdlib\ParametersInterface;
It's most likely just a behavior oddity in Eclipse, where the auto-recognition code for parameters is possibly not the same code that auto-recognizes returns. I would bet the parameter recognition is more naive, in that it probably keys specifically off your method signature. So, if your method signature is
public function executeService(string $serviceClass, ParametersInterface $inputParams)
then the auto-doc probably gets exactly just the ParametersInterface you have in the code.
However, if you wrote the code this way
public function executeService(string $serviceClass, Zend\Stdlib\ParametersInterface $inputParams)
then I'd bet the auto-doc would show the fully namespaced name.
It wouldn't hurt to report it to Eclipse (if it's not already been reported).
This may happen if you have use ParametersInterface; with it's path on top of file, so PHP doc takes it into account, but you don't have use ZF\Hal\Entity, so it's inserted in #return with full path.
In PHPStorm it works like this, so probably in Eclipse it works the same
Does anyone know any way to specify #return type based on the input parameter?
My project has a lot of method calls such as
...->getComponent('name')->someMethod()
where getComponent is a service locator and IDE doesn't understand which value it returns.
For the PhpStorm IDE, you can create one or more files called .phpstorm.meta.php in your project. Here is an example content of such a file:
<?php
namespace PHPSTORM_META {
override(\Factory::getComponent(), map([
"name" => \some\namespace\Name::class,
"exception" => \Exception::class,
]));
}
You can find the documentation here:
https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#map
I recommend committing this file to make auto-completion available to other PhpStorm users.
The only way would be to have getComponent()'s #return tag actually list out all possible data types that could return. This kind of dynamic, loose typing behavior that PHP allows for is not conducive to such static resolution as done by an IDE autocomplete. It has always been a tough case to try to document, because your API doc is just as "static" as the "object tree" that an IDE has to construct in order to provide autocompletion.
If IDE autocompletion is urgent enough to warrant adjusting the code to make it happen, then aside from my earlier
#return ClassOne|ClassTwo|...|ClassInfinitum option,
you could split the two calls rather than chaining them, and use an #var "local docblock" that some IDEs know how to recognize:
// this litte docblock establishes the data type for the local variable $nameComponent
// note however that some IDEs want "datatype varname",
// some want "varname datatype",
// and some don't recognize the docblock at all
/** #var \NameClass $myComponent */
$nameComponent = $service->getComponent('name');
// here, the known methods of NameClass should pop up after typing ->
$nameComponent->
I'm using PHPDoc. I've got a method that accepts a string parameter. That string should be the name of a class that implements a certain interface, say, IMyInterface.
Should I do:
/**
* #param string $class_name
*/
public function myMethod($class_name) {}
or
/**
* #param IMyInterface $class_name
*/
public function myMethod($class_name) {}
I'm guessing that since classes and interfaces aren't first class in PHP, it's probably the latter. But something seems wrong about both options.
I used ApiGen for a project I did a while back and I found it best to use the class name as it created linking within the documentation to the pages for those particular classes. I haven't used PHPDoc, but perhaps it has similar functionality and make your documentation a little more accessible.
/**
* Constructor function.
*
* Creates a new user object with data provided.
*
* #return void
* #param mixed An array or object of user information to be read in.
* #param Permissions An instance of a Permissions object to associate with the user.
*/
public function __construct($data,$perms) {
...
}
In your use case, "string" is correct, because you are telling the reader exactly what kind of datatype to pass in. If a user actually tried passing a concrete class instance of an IMyInterface implementer, your code is no doubt going to choke.
If your method is only built to accept a certain list of strings that represent a list of usable interfaces, then I'd suggest saying this outright in the method's long description, not in the one parameter's description. I would also utilize the #see tags to provide links to the docs of all the interfaces this method is built to accept. That way, your #param tag truly tells the reader "dude, I gotta have a string", your method description explains how the method will take that string and correlate it to a defined interface, and the #see tags will help the reader jump directly to any/all of the interfaces themselves.
Regarding IDE autocompletion, some IDEs can interpret "/** #var IMyInterface $localMethodVar */" inside your method code, and would then provide autocompletion on $localMethodVar as if it were a defined instance of IMyInterface.
In my ZF 1.11 application I'm storing my translator in registry like this:
Zend_Registry::set('Zend_Translate', $translator);
So in my view scripts I can access the translator this way:
$this->translate('abc');
Is there any clever way to be able to use this call instead:
$this->_('abc');
Using $this->translate clutters the views, and lot's of people are used to seeing _() anyway.
Whereas I generally agree with the notion that function/method names should be meaningful, I also agree that the _() for translations is a widely used standard and therefore acceptable.
You can do this by adding wrappers to your intermediate layers. For example the following would make the method available to all your controllers derived from MyProject_Controller_Action:
class MyProject_Controller_Action extends Zend_Controller_Action
{
protected $translator;
public function init()
{
$this->translator = Zend_Registry::get('Zend_Translate');
}
/**
* Translator wrapper
*
* #param string $string The string to be translated
* #return string $translated The translated string
*/
protected function _($string)
{
$translated = $this->translator->translate($string);
return $translated;
}
}
Of course the same can be done with Zend_View.
Disclaimer: It is not the best practice to clutter your code with direct calls to the registry. Actually it's an anti-pattern which should be replaced by DI. Zend Framework 2 will make it much easier for us to avoid the registry. This code could be improved by actually injecting the translation object into the class via constructor.
No, not that I know of. There are several implicit issues related to that anyway. First, you should always give functions (and variables for that matter) meaningful names. That said, __() is not a meaningful name at all. Quite the opposite, in fact, it has no meaning. Second of all, it is considered best practice to prefix only private and protected functions (and, again, variables for that matter) with an underscore.
Finally, with the way zend view helpers work, you would pretty much have to sorta "trick" the system into locating your view helper if it was named __(). You would have to name it something like Zend_View_Helper___ and that wouldn't work. Not to mention, that would entail having to name your file __.php.
I suppose you could name your helper Zend_View_Helper_T, in which case you could translate stuff using $this->t($string); (I tested this and it works), but again you should always use meaningful names.
Edit
Having not realized that you wanted to call this from within the controller until now, I decided to revise my answer and give a little feedback about the comment I received from the down voter..
It's hard to recommend that you create a wrapper class for Zend_Controller_Action in which to create a function _() for the following reason:
Because regardless it being an "accepted standard" or not, I
reiterate that that all methods and variables should have a
meaningful name. I must assert this because I am a firm believer in
following explicit coding standards (as opposed to those "hearsay"
or "recently-adopted" practices that don't directly correspond to a
known - and thereby trusted - paradigm). That said, should PEAR, or
even Zend, decide to adopt such a radical change some day, I will
resign my disposition. NOTE: It could be argued that credible
companies like Drupal and their self-proclaimed best practices could
be considered explicit coding standards but I disagree. Why? Because
PEAR is...well...it's PEAR. And Zend is "The PHP Company." It's hard
to get more credible than that. If anyone disagrees with that last
statement please state why or correct me instead of down voting. Regardless, standards
are merely a suggestion not required; therefore, they should be treated as
such. So, I guess as long as you're following some standard then that's good! These
are not rules after all.
Nevertheless, markus' solution was good other than the function name (for reasons stated previously). The only thing I would change is the call to Zend_Registry::get() in the _() function. If you plan to call that function as much as you alluded to, then something like this might work better:
class MyProject_Controller_Action extends Zend_Controller_Action
{
/**
* the translator object
* #var Zend_Translate
*/
protected $_translator;
public function init()
{
$this->_translator = Zend_Registry::get('Zend_Translate');
}
/**
* note my new method name, you don't have to use it but I still
* recommend it. the name is just a suggestion, if you prefer something
* like _translate() or _trnslte() then by all means (although I don't
* recommend abbreviations unless they're super obvious I guess).
*/
protected function _trans($string)
{
return $this->_translator->translate((string) $string);
}
}