PHPDoc and __callStatic - php

tl;dr
What is the correct way to annotate (in PHPDoc) functions implemented via __callStatic? More important: is there a way that will make NetBeans and PHPStorm understand that these are static methods?
Motivation
If you want the bigger picture, here's how I got to this question.
Problem: In my current project we have a ton of classes that should really be singletons (DB proxies and the like). Needless to say, we have at least a few hundred require_once and $foo = new FooProxy(); lines.
Solution: I created a Loader class to solve this, using the __callStatic magic method so we can just say $foo = Loader::FooProxy();. It's perfect for our purposes, but:
Problem: This way there's obviously no type hinting in either IDE used in the team.
Solution: Every module defines a subclass of Loader, adding methods that just route to __callStatic.
Problem: Adding actually interpreted code just for auto-completion's sake is not acceptable (this could be argued, but let's accept it for the time being).
Solution: Let's not add any real methods, only declare the methods in PHPDoc like this:
<?php
/**
* #method FooProxy FooProxy()
*/
class BarLoader extends Loader {}
?>
Problem: FooProxy is not a static method. None of the following make it static either:
<?php
/**
* #static
* #method FooProxy FooProxy()
*/
///////////////
/**
* #static #method A A()
* #method static A A()
* #method A static A()
* #method A A() static
*/
Making the class abstract makes no difference. About an hour of Google'ing turned up no solutions. The primary goal is to make the IDEs aware of these functions; having correct PHPDoc is not really a necessity.

Well, PhpStorm 3.0 will accept
#method static type name() description
See relevant feature request http://youtrack.jetbrains.net/issue/WI-4051

Generally speaking, I think the choice of using the magic stuff comes with caveat of having to accept a tradeoff of losing the effectiveness of things like autocompletion.
However, in my testing with Eclipse PDT (Helios with PHP 5.3.2 on WinXP), I was able to get good autocompletions from one explicit static method and two magic static methods out of my Loader class that I modeled after your example.
In short, it appears the use of the #method tag in the class docblock was enough for Eclipse to figure things out. If NetBeans and PHPStorm are having trouble, I'm not sure if it's related to the "static" aspect or not... it may just be that the parsing of such dynamic code may be more than their autocompletion logic is built to handle.
<?php
/**
* #method BarProxy BarProxy() returns an instance of BarProxy
* #method BazProxy BazProxy() returns an instance of BazProxy
*/
class Loader
{
public static function __callStatic($name, $arguments)
{
return new $name($arguments);
}
/**
* #return FooProxy
*/
public static function FooProxy(){
return new FooProxy();
}
}
class FooProxy
{
public function sayCheese() {}
}
class BarProxy
{
public function eatFries() {}
}
class BazProxy
{
public function sleep() {}
}
$foo = Loader::FooProxy();
$foo->sayCheese(); // did this simply to verify explicit autocompletion succeeded
$bar = Loader::BarProxy();
$bar->eatFries(); // autocompletion of just "$bar->" brought up "eatFries()"
$baz = Loader::BazProxy();
$baz->sleep(); // autocompletion of just "$baz->" brought up "sleep()"

Related

Is there a way to indicate that a class has magic methods defined for every method on another class?

Is there a way to document that a certain class has magic methods for every method defined in another class?
I am using PhpStorm, so I would be happy with any solution that will get autocomplete to work properly for that.
class A
{
// a bunch of functions go here...
}
/**
* Class B
* What should go here to make it work???
*/
class B
{
private $aInstance;
public function __construct() {
$this->aInstance = new A();
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
if(method_exists($this->aInstance, $name)) {
return $this->aInstance->{$name}(...$arguments);
}
throw new BadMethodCallException();
}
// a bunch more functions go here...
}
The proper solution is to use supported #method PHPDoc tags. This way it will also work in other editors/IDEs that support PHPDoc and understand such standard tag.
This approach requires every method to be listed separately. More on this in another StackOverflow question/answer: https://stackoverflow.com/a/15634488/783119.
In current PhpStorm versions you may use not-in-PHPDoc-specs (and therefore possibly PhpStorm-specific) #mixin tag.
Adding #mixing className in PHPDoc comment for your target class should do the job for you.
/**
* Class B
*
* #mixin A
*/
class B
{
Basically, #mixin tag does what actual PHP's traits do.
Please note that there is no guarantee that support for such tag will not be removed at some point in the future, although it's pretty unlikely.

Is it correct using __call() instead of getters and setters?

On the subject of good practices, is it right to use __call() to classes that need getters and setters to their properties?
See an example of Doctrine Entity:
https://gist.github.com/devmatheus/10668172#file-sessao1-php
I know that performance will be affected but this will decrease time in programming, what do you think?
As you said, the performance will be decreased, but better design always beats performance. A Server with more Ram and CPU isn't so expensive, like a Developer.
I think it is a good point, if you need dynamicly invoked methods. , but then, it should be well documented via PHP Doc (It helps IDEs for Code Completion, and Developers don't need 1 hour to understand). This will only be the case, if you're not able to document it with this PHP Doc (What is the Methodname? What is the return type?)
/*
* #method myReturnType $myMethodName
* #property myReturnType $myPropertyName
*/
class Foo {}
If your Method can be staticly created, you should do this.
Your Gist is talking about getters and setters, that are automaticly created via __call, with no extra logic. The good thing about getters and setters is, you can work with the value, before you set or get it. In this case, you can not. So I would consider this bad design. If you would make the fields public instead of protected, it would have the same result.
Side Note: IDE's like PHP Storm can automaticly create Setter and Getter for you, if your wrting protected $_foo; (In this case simple press ALT+ENTER on the field.
Result:
protected $_foo;
/**
* #return mixed
*/
public function getFoo()
{
return $this->_foo;
}
/**
* #param mixed $foo
*/
public function setFoo($foo)
{
$this->_foo = $foo;
}
If you define the Type via PHP doc or via direct assignment (protected $foo = true), the generated PHP Doc even writes #param bool $foo and #return bool instead of mixed.

Eclipse IDE: Code completion for the private and protected properties accessed by magic functions

As it states that it's ok to answer my own question and having found some trick to achieve code completion in Eclipse IDE (for PHP) I would like to share this with other users.
I found some solution here but somehow I can't make doxygen work properly with such declared properties, I found some workaround.
The problem
If you use magic methods, especially for property overloading __get(), __set(), (probably it concerns also __call() and __invoke()), there is a problem, that property names defined in somehow dynamic (as variable variables) and not visible in any scope. It is possible because their names are passed as strings and can be manipulated in any way.
So it is not possible to have those magically accessed properties (or methods) in code completion, and in my case (don't know why, but it doesn't matter), in the generated documentation of the class.
(The code-completion is when the editor helps the programmer by showing all possible properties and methods, thus letting avoid some errors.)
Consider we have the following class:
/**
* Test class
*/
class myClass {
private $field_someValue1;
private $field_someValue2;
/**
* Magic method getter for hidden properties.
* #param string $name
* #return mixed
*/
public function __get($name){
switch($name){
case 'foo': return $this->$field_someValue1; break;
case 'bar': return $this->$field_someValue2; break;
default:
// handle non-existing property error here
}
}
/**
* Simply do nothing, but be some public part of the class.
* #return void
*/
public function doNothing(){
}
}
$a = new myClass();
So in my code when I type $a-> the editor should help me by hinting that there are two properties for my object: foo and bar. Of course it can't show $field_someValue1 and $field_someValue2 as they are private.
How to make Eclipse help me?
The solution
My solution (or rather workaround) is to create a new file that will contain the same classes names as in the main files of the project. I created a special directory for that.
There I can easily declare all the hidden properties I access via __get() (__set(), __isset() etc.) like this:
class myClass {
/**
* Some accessible hidden property.
* #var string $foo
*/
public $foo;
/**
* Another accessible hidden property.
* #var bool $bar
*/
public $bar;
}
And that's all.
This makes Eclipse allow help me by hinting the class's properties (it combines all properties of two same-named classes) and also gives doxygen the proper info for documentation.
The problem is doxygen will generate this file in its "Files" section; if you don't care, it's ok.
Please note that in my "documentation file" I did not need to re-type the doNothing() method as it's visible (because it's public).
Hope this helps to anyone.
More elegant would be use phpdoc #property in the same class file:
/**
* Test class
* #property foo $foo
* #property bar $bar
*/
class myClass {
private $field_someValue1;
private $field_someValue2;

DocBlock class type inheritance

Although this question is about DocBlocks in general, my use-case is about PHP.
Consider the following PHP code:
<?php
class ParentClass {
/**
* Says 'hi' to the world.
* #return ParentClass Returns itself for chaining.
*/
public function say_hi(){
echo 'hi';
return $this;
}
}
class ChildClass extends ParentClass {
/**
* Says 'bye' to the world.
* #return ChildClass Returns itself for chaining.
*/
public function say_bye(){
echo 'bye';
return $this;
}
}
$c = new ChildClass;
$c->say_hi()->say_b| <- type hinting won't suggest "say_bye" here
?>
This is just a trivial class with some chaining. The extended class looses type hinting because the parent class' docblock is making use of a specific class name which does not have the methods/properties of the child class.
Assuming we do want type-hinting capability, (if not, please leave this question - I don't want useless arguments here), how should I fix this?
I came up with the following possibilities:
Change the PHPDoc standard to allow a special keyword
Add a superfluous say_hi() method which calls the parent just to redeclare docblock
Do not specify the return type at all, let the IDE decide what return $this; means (does this even work?)
You can solve this like this :
class ParentClass {
/**
* Says 'hi' to the world.
* #return static
*/
public function say_hi(){
echo 'hi';
return $this;
}
}
The "#return static" statement allows exactly what you want, PhpStorm works just fine with it.
What you describe is commonly called a "fluent interface", where all of an object's methods do their work and return the object itself.
I have not personally seen any PHPDoc guideline finalized on how to do exactly that. As such, I'm not aware that any IDE has put forth a means for its autocomplete functionality to handle the use case.
A likely path that PHPDoc will take on this is to utilize "#return $this" to be the convention to indicate fluent methods, as it matches the code syntax itself and is therefore very clear. I doubt that any IDEs will build that capability in until the standard itself incorporates this use case.
In the short term, I think that your superfluous "ChildClass::say_hi(){parent::say_hit();}" might get your IDE autocompletion to work. Again, might, because having the autocomplete also recognize method chaining itself (e.g. $foo->bar()->baz()->roll()->tide();) might not exist.

Proper Way to Document Class in Netbeans PHP

For reasons of ease of maintenance AND IDE class auto-completion and member hinting, I've used PHPDoc in my project. Given this example class:
class my_class {
public $id;
public $name;
public $number;
public function __construct() {
//Do something
}
public function Rename($name) {
$this->name = $name;
}
}
I would prefer to document all properties ($id, $name and $number) with the class documentation itself, which is above the class declaration, and then place documentation for methods (if necessary) above each method. Here is what I ultimately want my class to look like:
/**
* Represents an example class for Stackoverflow
*
* #property int $id The id of the object
* #property string $name The name of the object
* #property int $number The number of the object
*/
class my_class {
public $id;
public $name;
public $number;
public function __construct() {
//Do something
}
/**
* Renames the object
* #param string $name Name to rename object
*/
public function Rename($name) {
$this->name = $name;
}
}
This is precisely what I prefer to have as documentation, however Netbeans' autocomplete fails to operate correctly, as it lists each property twice. For example, if I start typing $my_class_object->i the auto-complete will list two $id properties: one as described in my PHPDoc, and another is described as an unknown variable with "PHPDoc Not Found".
There is a solution that works to solve the Netbeans issue - add a #var PHPDoc block above each property, however I think it unnecessarily clutters up my class, especially some of my classes that have 10+ properties.
Is there a [good] solution to both of my issues (clean doc, proper Netbeans hinting), or am I going about this incorrectly?
The "property" tag is specifically and explicitly for "magic" properties, meaning any that don't actually appear in the code itself. That's the key reason why the tag occurs only in the class docblock. As such, I'm guessing IDEs that recognize the "property" tag do so from that "it's NOT seen in the code" perspective. Granted, I could understand an expectation that autocomplete should recognize the existence of such a property, and therefore make it available for you. However, my bet is that the IDEs will stick with using only the code itself to build a model, and only use docblock info to supplement the elements that it already sees in the code.
Using the "var" tag is the one proper way to document your "coded" properties. If you want to minimize the lines required in order to use that tag on all the properties, use a one-line docblock:
/** #var int */
public $id;
Also, you could use the docblock template to cut down on docblocks, where tag similarity fits your code:
/** #var string */
public $name;
/**##+ #var int */
public $id;
public $number;
/**##-*/
That doesn't seem like much savings in this short list, but it does help when there are lots of properties. Also, it works fine around methods.
I prefer to use #var above each property and no #property at all. I feel that this allows you to more closely associate the comments with the thing that is being commented on. I.e., the comments for a property are always right next to the property. If you're using the #property style and you've got a big class with a ton of properties, it's entirely possible that the comment which describes a property is pages away from it.
I am not sure about the exact syntax but I am sure that netbeans will adhere to the standard php documentation.
http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.pkg.html
http://www.phpdoc.org/

Categories