-
-
Notifications
You must be signed in to change notification settings - Fork 635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce procedural / functional @implements tag.. #1689
Comments
I just wrote a topic on discussing things on the PHP-FIG mailing list but this is the phpDocumentor issue tracker itself; which is home to experimental stuff ;) |
I invite anyone here to comment on this issue but I am interested in the opinion of other FIG members before diving too deep into this request right now |
I don't speak for them, but I guess WordPress should be interested in this, since most of the application's architecture is based on hooks. Due to their backwards-compatibility policy, that's most probably going to stay for many years to come. There are probably several other legacy systems with similar architectures that could benefit from |
+1 to |
Reading the discussion on PHP-FIG: https://groups.google.com/forum/?fromgroups=#!topic/php-fig/49ZfuJ1iisg I think the best would be to use Or or or or something like that
|
Please have a look at the linked issue as well. The |
I like The other option I see is using something that works with function fooProto(X $x) {}
/**
* @param X $x
*
* @ŧype function:fooProto()
*/
function fooImpl(X $x) {}
/**
* @var function:fooProto()
*/
$callback = 'fooImpl';
/**
* @param function:fooProto() $callback
* @param X $x
*/
function callFoo($callback, X $x) {
$callback($x);
} Otherwise we would have different constructs for callback variables and the actual function declaration. |
Other interesting questions:
Example for the second question: function fooProto(X $x);
function barProto(X $x);
/**
* @param function:fooProto $callback
* @param X $x
*/
function callFoo($callback, X $x) {
callBar($callback, $x);
}
/**
* @param function:barProto $callback
* @param X $x
*/
function callBar($callback, X $x) {
$callback($x);
} |
Note that the specification of a function/method is already possible with the /**
* @param \global_function $p
* @param \GLOBAL_CONSTANT $p
* @param \$global_variable $p
* @param \GlobalClass $p
* @param \Name\Spaced\function $p
* @param \Name\Spaced\CONSTANT $p
* @param \Name\Spaced\Class::$property $p
* @param \Name\Spaced\Class::function $p
* @param \Name\Spaced\Class $p
*/ Note that you could include brackets for the functions if you like. All in all, there is no need for the function prefix. The problem with the I am still in favor for the
/**
* @provides \Psr\Log\LoggerInterface
*/
trait LoggerTrait {}
function fooProto(X $x) {}
/**
* @provides \fooProto
*/
function fooImpl(X $x) {}
function fooProto(X $x) {}
/**
* @provides \fooProto
*/
$cb = function (X $x) {} @donquixote That is why I think that the things are related, we can reuse and combine it. 😊 |
This may be true, but so far I don't think there is a common agreement what it means to use a function FQSEN as a
Ok. So we could allow function signature type specifiers in
I am ok with this, but regard it as independent from this discussion.
I understand the confusion (as stated before), although to me it simply means we would be bold and extend the meaning to a new domain. This would be a creative and autonomous action, but I don't see it creating a real conflict - except what I said in my previous comment about inheritance and such.
I am not very convinced of this term. To me, "provide" is a fuzzy word, and I somehow associate it with "give". E.g.
I think here I would work with @type / @var, because a closure IS a variable. |
Here it gets funny.
(see code example 3 posts before) |
Ummm.. @param does not support aan fqsen. It has a Type as first argument |
@mvriel oops you are of course right, it does not support FQSEN. Would it be a problem though? On the other hand, allowing it also creates confusion: /**
* @param \Name\Spaced\Class::function $cb
*/
function fn(callable $cb): string {} Well, what is correct now? How do we verify (statically) that things are as they should be (easy and fast)? I don't know if this is feasible or even possible. Maybe you can shed some light on this matter. Note that the function prefix within @param has the same problem. However, we should think about FQSEN support and other reusable and well known syntax before introducing new kinds of prefixes; e.g. we could use generic syntax: /**
* @param callable<\Name\Spaced\Class::function> $cb
*/
function fn(callable $cb): string {} But I dislike this version compared to the previous one because we repeat callable and did not really solve the problem.
Actually I fully agree with you that it does not create confusion. For me the things with implements is crystal clear because it depends on context, like the extends keyword does.
I do not share this association but it seems like a bad keyword if it could create such confusion.
You are right and I agree that the @var would be sufficient. However, it has the same problem as described before and we would need to either support FQSEN (and IDEs would need to check on their own what kind it is) or something else that tells the full story. Btw. we could also say that functions actually require the brackets. But that would be different to how FQSENs are currently parsed through most tools and IDEs. |
Well, first of all, "class" and "function" are reserved words, so i am going to rename them in the example.. Also, I like to add the "()" brackets to the type specifier. So, your modified code sample with some extra stuff: /**
* @param \N\C::fooProto() $f
* @param \Animal\Duck[] $ducks
*/
function fn(callable $f, array $ducks): string {} For both parameters, the PHP type hint is different from the doc type specifier. For both parameters, the doc type specifier is more specific than the PHP type hint, because phpDocumentor has a higher expressiveness than PHP itself. And in both cases, when analysing, the more expressive version wins. Or where do you see the problem?
Well.. I prefer the brackets, makes it more obvious to everyone on first sight.
Currently, my IDE (PhpStorm, who would have guessed) does not support functions in And if not: What is the worst that could happen, really?
Imo, we should forget about this "everything is an fqsen". In @param, we could say "everything is a type", and then we simply need to say that a callback signature specifier IS a type. Yes, it should be specified with the fully qualified namespace, but I don't care if the complete thing including the brackets is considered an fqsen. It is a type (if we interpret it as such), that is enough. But, all of this said, this does bring me to a different concern. If we consider Or is this too much of a leap? Would the more natural/intuitive understanding be "the callback that references |
I am not a big fan of making a FQSEN count as a Type; it seems to me that we're trying to shoe-horn a desired feature in the wrong place. As I see it, what you want is to provide meta-data about the Type (which is callable). Have you checked how other languages solve this issue? I can imagine that JSDoc or RDoc may have hit this bump before? If other languages do not have a similar construct then I propose we use the Inline PHPDoc style together with a new tag to indicate what the expected signature for a callback would be. For example something like this:
Though I must address that we are now discussing two things at the same time:
I suggest focussing on the former for now and starting a new discussion for the second since they seem to be only remotely related to eachother |
I think these two things depend on each other, and that at least for a while it is better to discuss them in the same issue. But if you disagree, we could open a new one for the
Well.. what I suggest is not that every FQSEN is a Type, but that
Well..
https://en.wikipedia.org/wiki/Type_signature This is quite interesting. |
All concerns I raised are more of a rhetorical nature and not true concerns of mine. All variations work, the question is, which variation is the one that is best for tools and IDEs.
I also prefer it with the brackets to make it more clear. PhpStorm does not care; how does phpDocumentor handle that?
It also collides with namespace and class constants because casing does not matter. However, I do not really see an issue here because tools would need to look the thingy up anyways and display an error if it cannot be found, hence, if it is found and is e.g. a constant or a class or whatever thingy that makes no sense an error needs to be issued as well. No problem there imho.
Well, JSDoc has the @callback tag and uses it within the @param tag. We do not really need the @callback tag because all structural elements are automatically addressable via their FQSEN and that is why I would extend the @param type to support FQSENs. I do not think that this is tucking in of a feature, on the contrary. It makes imho absolute sense to allows them because we already allow FQCNs and we simply extend them. This would also allow other interesting things like giving a range of constants that are allowed. However, as always I would like to see support for short names as PhpStorm supports it.
It is an FQSEN, specifically for a method but we would need to support functions as well (PCRE but not necessarily correct):
Writing The following example is valid PHP code: <?php
namespace N {
class C {
public function cb() {
return 'called';
}
}
}
namespace {
/** @var \N\C::cb() $cb */
$cb = [ new N\C(), 'cb' ];
/** @param \N\C::cb() $cb */
function fn(callable $cb) {
echo $cb();
}
fn($cb);
} |
That would be the other way, when you want to indicate a caller how the callable would be called. I think that is even more useful than the feature described in my last comment. However, I think that the syntax your propose is overly complicated. Why not simply use PHP 7 syntax? /**
* @param {callable(int $a, int $b): string} $cb
*/ Note how I use the braces that we are currently discussing to allow specification of the return type as well. The callable cannot clash with any user defined thingy because it is a reserved name and the signature can be parsed with a normal PHP signature parser. Of course, more complex types cannot be expressed, namely any complex type that cannot be expressed with PHP 7. But I think that it is sufficient to keep that in the description than. |
I have an interesting idea extending this: /**
* @param callable $f {
* @param int $a
* @param string $b
* @return X
* @throws FooException
* }
* @return X|null
*/
function foo(callable $f) {
try {
return $f(5, 'x');
}
catch (FooException $e) {
return null;
}
} This would give us the full expressiveness of phpDocumentor, and allow even explanation text. Otherwise, if we stick to native PHP signature syntax, I find the syntax suggested in Fleshgrinder's last post shorter and preferable. |
In the big picture, I now see the following partial proposals, which can all coexist, mostly, and each have their own specific use case:
If we can agree on a plan similar to this, we can then discuss the details in separate issues. |
May I suggest you use something like /**
* This is a summary
*
* @param string $var A pub in London.
* @return callable
*/
function named($var) {
/**
* This is a summary
*
* @type literal
*
* @param string $val A restaurant in London.
* @uses string $var A pub in London.
*/
return function($val) use($var) {
echo "I went to $val and then $var";
};
} |
I don't quite get it.. |
Let's continue part of the discussion over at #1712. This leaves for this discussion:
|
function my_func() {
// Inside the my_func() definition.
} |
@henrywright Finally, what is the benefit of knowing something is an anonymous function, and not another type of callable? |
I see your point @donquixote. Sorry to have derailed the conversion. |
The trait A class could additionally denote via A disconnected function X that In fact, it's all one and the same. (Especially because No need to limit this to certain language constructs. Every case has the identical meaning: fulfilling the specified contract. Various other PSR-5/PHPDoc constructs accept a mixture of FQSENs, referencing and triggering different meanings. Exactly the same happens here. Arguing for this proposal doesn't mean that I'm a fan of the architectural pattern, nor would I advise anyone to base their application's architecture around such loose callbacks today. Even the OP made these points crystal clear. The subjective interpretation of |
@sun I mostly agree, just a few comments.
For traits, in some cases, it could actually make sense to document specific methods with
Generally this would be just a duplication of the native
Yes. Of course, a method/function can not implement an interface, and a class cannot implement a method/function.
What we do in Drupal and Wordpress may be a bit rotten indeed. But the concept of "functional programming" is still valid in modern times. Especially with the addition of closures/lambdas. |
For some time I have been wondering if we lose semantic information by switching from a one-method OOP interface to a callback signature which only specifies parameter types and return type. Here someone had the same question: In previous comments I said that we should distinguish The accepted stackexchange answer proposes to use value objects / value types to make a signature more specific and more semantic. I think we still want both in PHP, because we cannot expect everyone to use value objects / value types. But it is still interesting read related to this discussion :) |
This suggestion is based on an observation in Drupal 7.
It is becoming less of a problem with more and more code being OOP (e.g. moving to Drupal 8), but it might still apply to some people or more, for some time or longer.
Problem: The framework (or CMS) forces a specific parameter signature for functions with a specific role.
In Drupal 7 these are hooks, theme functions, form builder functions, etc.
The IDE does not know this, and complains about unused parameters.
For a class method overriding an interface method, it does not care if parameters are unused, because it understands that the parameters are required. But for procedural, this does not work.
The official way to document this in Drupal 7 is "Implements hook_foo()". But this is too much of a Drupalism to suggest support by the IDE or phpDocumentor. Also, it only applies to hooks, not to theme functions or form callbacks.
So I am going to suggest an
@implements
doc tag for functions, methods and closures - equivalent to the native "implements" statement, but on function level.The
@implements
tag would indicate that the function has the same (or weaker) signature as the one it implements, in the same way that a class method must have the same (or weaker) signature as the interface method it implements.An IDE that understands this could
This is how it would look like:
Old discussion on drupal.org: https://www.drupal.org/node/983268 (I am not promising anything about whether the Drupal community will pick this up)
Doxygen has something like it, but I don't quite understand how it is meant to be used..
http://www.stack.nl/~dimitri/doxygen/manual/commands.html#cmdimplements
EDIT: I just realize there is a github user with this name :)
Code formatting of
@implements
helps.The text was updated successfully, but these errors were encountered: