I'm working on a library which uses the phpDocumentor specification on determining the type system using reflection. However, I couldn't find any information about generic type declarations.
Is there any way a generic type declaration should be specified?
For example: is there any specification (in-progress) which specifies anything like:
/**
* #template <T extends Base>
*/
class Collection {
/**
* #return Iterator<T>
*/
function get_iterator();
}
Note that the code above is just an example to illustrate what I mean by generic type declarations. I do not want this achieve anything to do with collections and iterators.
There apparently was some interest in the related topic of object specialization because that was added to PSR-5:
generic = collection-type "<" [type-expression "," *SP] type-expression ">"
Edit: and then removed as Jack notes below, albeit with a stated expectation that they'll return in one form or another before it's standardised
However, it doesn't provide a means to define a class as generic, and PSR-5 has been abandoned for a long while. The de-facto standard, phpDocumentor, does not in any way define support for such syntax.
If you want to informally mark up generics, you're best off making up your own syntax (inspired by similar syntax that already exists like Closure's documentation or JSDoc) while carefully avoiding anything that could actively confuse phpDocumentor, something like:
/**
* #template {Base} T
*/
/**
* #return Iterator {Iterator<T>}
*/
If you want to do it formally, you'll need to switch to another documentation system like doxygen.
You might want to check out the psalm project.
https://psalm.dev/docs/
https://github.com/vimeo/psalm
It supports generics in the documentation, as well as so much more. It's also used by Symfony's DB ORM, doctrine.
It supports Emacs, Phpstorm/Intellij, vim, and visual studio
Psalm supports this via
/**
* #template T of Foo
*/
Ref: https://medium.com/vimeo-engineering-blog/uncovering-php-bugs-with-template-a4ca46eb9aeb
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.
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);
}
}
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.
I've written a PHP extension in C, and I want to create PHPdoc documentation so that my users will get inline docs in their PHP IDE (in this case, Netbeans) when calling my extension.
Ideally I'd like to do this by embedding the PHPdocs in the C code, to keep implementation and documentation together.
Assuming it's possible to embed PHPdocs into the C, what extra steps are needed to make the documentation appear in Netbeans (as it would for PHPdocs of PHP code)?
edit:
O'Reilly Programming PHP refers to the /* {{{ proto comment format being used in doc generation, though I'm not sure if the referred scripts generate PHPdocs:
The {{{ proto line is not only used
for folding in the editor, but is also
parsed by the genfunclist and
genfuncsummary scripts that are part
of the PHP documentation project. If
you are never going to distribute your
extension and have no ambitions to
have it bundled with PHP, you can
remove these comments.
One approach that works is to have a PHP file with stub functions with the appropriate PHPdocs, then DON'T include it in the PHP application, but DO add it to Netbean's PHP include path (in File->Project Properties->PHP Include Path).
This way type completion and inline docs work, but PHP isn't confused by multiple declarations of the function.
This seems a bit hacky, since it would be good keep the docs in the same file as the implementation, but it does actually seem to the correct approach, since that's how the built in functions and extensions are documented - see ~/netbeans-6.7/php1/phpstubs/phpruntime/*.php
eg:
In the C file:
PHP_FUNCTION(myFunctionStringFunction)
{
// extension implementation
}
And then in a PHP file, the stub declaration:
/**
* My docs here
* #param string $myString
*/
function myFunctionStringFunction($myString)
{
die("Empty stub function for documenation purposes only. This file shouldn't be included in an app.");
}
you only need to use the right TAGS inside your Comments.
/**
* Returns the OS Languages for an Subversion ID
*
* #access public
* #param int $subVersionId Subversion ID
* #return array $results Languages for Subversion ID
*/
You can find all available tags on the documenation
PHPDoc
I think it is possible to use Reflection API to generate the prototype file, though I could not find existing code that does exactly that.
As written in extension skeleton:
/* {{{ */ and /* }}} */
The previous line is meant for vim and emacs, so it can correctly fold
and unfold functions in source code. See the corresponding marks just
before function definition, where the functions purpose is also
documented. Please follow this convention for the convenience of
others editing your code.
I think my eclipse's ctrl+clicking links might benefit greatly...
Edit: I'm using eclipse PDT.
Edit 2: I'm very happy with the solution of putting docblocks before functions (and variables) with an #return or #var statement, I've just updated the documentation of my app and now eclipse is showing me what functions are available to what objects!
Awesome.
// [...]
/**
* Return the Request object
*
* #return Zend_Controller_Request_Abstract
*/
public function getRequest()
{
return $this->_request;
}
// [...]
works perfectly with Eclipse PDT. Which plugin do you use?
Short answer: no.
Long answer: consider adding docblocks with #return declarations.
The only way to hint return type in PHP is to use a good IDE like Eclispe PDT or Zend Studio with standard comment block. PHP simply can n not predict return type because it is dynamically typed language so type checking is done in the run time unlike for the statically typed languages like C#, JAVA and C++.