I have class A with some method that returns bool value. I made phpdoc for that method.
I made another class B which extends class A and overrides method. New method returns string.
(I understand that in common case change the return type is not a good idea, but in my case it's a good way.)
I want to make phpdoc for new method. I could use {#inheritDoc}, but it takes full phpdoc from old method. I want to take from old phpdoc general method description and specification of arguments, but change description of return value.
How could I do it?
Based on the phpDocumentor manual for inheritance methods will inherit without the need for an inline {#inheritDoc} tag and you can override tags as you see fit.
Inheritance for methods functions similar to classes and interfaces.
When a superclass of the current class contains a method with the same
name (hence, this method is re-defined) then the following information
is inherited from that overridden method:
Summary
Description
The following tags:
author
copyright
version
param
return
throws
As with classes, each of the above will only be inherited if the
redefined method’s DocBlock does not have the element that is to be
inherited. So, for example, if the DocBlock of the redefined method
has a summary then it will not receive the overridden method’s
summary.
However from experience IDE's handle parsing this data in their own way and hence results may vary. For example, in IntelliJ/PHPStorm the parent method docs will be inherited but you can either override everything or nothing.
Related
I'm working on a PHP library and I have two classes.
First one is like this:
/**
* #method static A getInstance()
*/
interface A {...}
Second one is like this:
class B implements A {...}
getInstance is not defined in A, except in PHPDoc above class. Now, my IDE (PHPStorm) suggests an error, saying that I should implement getInstance in B.
My questions are:
Why they put this method in PHPDoc even though it's not declared in the interface? I doubt it's a mistake, since they used the same comment in many other classes.
What do you recommend? should I implement in B or should I remove the PHPDoc comment from A?
I think it's the mistake because any of the classes which implement OW_Storage interface don't implement getInstance() method. You can make sure it with help search. It means this function isn't used.
You can remove the doc block from the interface but right way is to do pull-request in origin repository.
Recently I got someones PHP site project (and this someone don't wan't to help me), so I have to understand his code. And maybe my answer would be stupid, but...
But there's some methods before class, that are doxumented as in example:
namespace Base\Classes;
/**
* #method int method1()
* #method $this method2(int $parameter)
*/
class SomeClass extends ParentClass
{
public $_s_a = false;
public $_user_roles = [];
public function SomeClassMethod() {
somethingDone();
}
}
And as you can see in this example, these documented methods are not implemented in defined class. But what my question is about - this methods are called from another classes and templates. And PHPStorm (my IDE) connects this documentation lines with calls, and ctrl+B leads from between references. But I can't find exact implementation of this methods. They cannot be found in parent classes, they are not in this file. And I thought maybe this is some syntax sugar that I'm not familiar with. Am I right? Or there something I'm missing, and all implementations somewhere in another place? (search by method name in folder gives nothing for me)
PHP has a few magic methods, and one of them is __call().
When you have an object that implements __call() (by itself or by one of the parent classes), you may call an inaccessible method on it, and the __call() method will be called instead. This happens, for example, when you call a private method from the outside, or when you call a method that was not defined in code.
When you use such calls to inaccessible methods, IDEs will most likely show a warning that the method does not exist, although the code itself will probably work at runtime. These warnings are quite annoying, so you can add a #method tag to your class, and the IDE will know that this method exists, and will not show a warning.
So, to support the code that you got from someone, take a look at the __call() method implementation. Be aware that this method may be implemented in one of the parent classes, so check them out as well.
Here's a couple of snippets:
Overriding constructor method has an extra parameter.
class Cat {
function __construct() {}
}
class Lion extends Cat {
function __construct($param) {}
}
Overriding (regular) method has an extra parameter.
class Cat {
function doSomething() {}
}
class Lion extends Cat {
function doSomething($param) {}
}
The first would work, while the second would throw Declaration of Lion::doSomething() should be compatible with that of Cat::doSomething().
Why the special attitude towards constructor methods?
To understand why they are treated differently, you have to understand Liskov's Substitution Principle, which states
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T." - BarbaraLiskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988).
In a nutshell this means any class using your Lion or Cat should be able to reliably call doSomething on it, regardless of the class being one or the other. If you change the method signature, this is no longer guaranteed (you may widen it, but not narrow it though).
Very simple Example
public function doSomethingWithFeline(Cat $feline)
{
$feline->doSomething(42);
}
Since Lion extends Cat, you established an is-a relationship, meaning doSomethingWithFeline will accept a Lion for a Cat. Now imagine you add a required argument to doSomething in Lion. The code above would break because it is not passing that new param. Hence, the need for compatible signatures.
LSP does not apply to constructors though, because subtypes might have different dependencies. For instance if you have a FileLogger and a DBLogger, the ctors (constructors) of the first would require a filename, while the latter would require a db adapter. As such, ctors are about concrete implementations and not part of the contract between classes.
The Liskov Substitution Principle states that "if S is a subtype of T, then objects of type T may be replaced with objects of type S". In your example it means that you should be able to replace objects of type Cat with objects of type Lion.
This is the reason your second code is not allowed. You would no longer be able to do this substitution as you would no longer be able to call the ->doSomething() method without arguments.
The constructor on the other hand is not subject to the Liskov Substitution Principle, as it is not part of the resulting object API. You will still be able to substitute the resulting objects, regardless of whether the constructor signatures match.
It is actually quite common that subclasses have more constructor arguments, because they are more specific and need more dependencies.
The __construct() as they are unique per class. The constructor for Lion is not the same constructor for Cat, although if Lion extends Cat and Lion did not have a __construct(), you could still extend the parent __construct() with Lion::__construct().
Unlike with other methods, PHP will not generate an E_STRICT level
error message when __construct() is overridden with different
parameters than the parent __construct() method has.
PHP Manual: Constructors and Destructors
The other Magic Methods take specific arguments, which means their argument counts, etc, will always be consistent.
After the classes are instantiated, then does polymorphism/overiding kick in for your doSomething(). Your parent class, like how an abstract class does, defines arguments and visibility which needs to be matched by the child class to support overriding.
The constructor is for the concrete object only. It gives birth to it.
The other methods are - as long as the subtype is concerned - in relation to the inherited class therefore those methods are part of the same object and therefore should be compatible when spread over the definition of multiple classes.
Otherwise you create an object that is a bit shizo.
Edit: If you also want to introduce strict checks with the constructor signature you can make use of an interface since PHP 5.2 which added constructor checks:
Added support for constructors in interfaces to force constructor signature checks in implementations. Starting with PHP 5.2.0, interfaces can have constructors. However, if you choose to declare a constructor in an interface, each class implementing that interface MUST include a constructor with a signature matching that of the base interface constructor. By 'signature' we mean the parameter and return type definitions, including any type hints and including whether the data is passed by reference or by value.
(take from: Other Enhancements - Migrating from PHP 5.1.x to PHP 5.2.x)
What you are describing is overloading which is not supported by PHP. Now when you are creating a constructor for a class, it is only used within that class and the parent constuctor is not called by default (see Constructors and Destructors. You need to call it manually parent::__construct().
When you create a method in your class it does access the parent method directly.
So in conclusion:
the constuctor isnt using overloading but is only for the class itself
the method is using overloading and therefor not allowed
How can we have autocomplete on property when this one isn't define in the same php file.
for example, with ZF, in the controller we can do
$this->view->voiture = new My_Voiture();
and in the view, we have a variable $this->voiture, but how can i have the autocomplete on it ?
i try /* #var $this->voiture My_voiture */ and no result...
for the moment, my answer is to do in the view
/* #var $voiture My_Voiture */
$voiture = $this->voiture;
but i don't like it. Have you better ?
Documenting the original variable in its original source is actually the best way to go, I think. All other usages of your view in other files should inherit the autocompletion just by documenting it at its one source.
In order to get the autocompletion that I would expect, I would do these things:
In the My_Voiture class, make sure you have docblocks for your variables and your methods. This isn't technically necessary for the autocompletion itself, but it will allow the autocompletion popups to contain much more info that just variables and methods.
In the My_View class, where $voiture is first declared (not used), I would place the #var docblock that identifies its type as My_Voiture. This should be enough to make any usage of its $voiture variable inherit the properties of My_Voiture.
In the My_Controller class, where $view is first declared (not used), I would place the #var docblock that identifies its type as My_View. This should be enough to make any usage of its $view variable inherit the properties of My_View.
Now, in your code file where you are expecting autocompletion on $this->view->voiture -- if it has nothing in it that indicates that $this is a My_Controller object, then Eclipse has nowhere to start when it tries to identify $this (and then all of its properties). I think I've seen some MVC code before where this is prevalent, due to much "dynamic" properties relying on "variable variables" like $$foo.
I am using php 5.3, and yes, there is a bug open for that, but some think this is not a bug, and this makes me wonder.
abstract class A{
private function bobo(array $in){
//do something
}
}
class B extends A{
private function bobo($shmoo,$shmaa){
//do something
}
}
This throws an error. Shouldn't inheritance ignore private methods?!
'Declaration of B::bobo() should be
compatible with that of A::bobo()'
Note that the bug report is slightly off, as PHP will log this message any time you have an error level of E_STRICT (or, more recently, regardless of your error level provided that you've set a custom error handler).
PHP's visibility rules clearly demonstrate that a child lacks the ability to see its parent's private members, which I doubt is all that surprising to anyone. If the child can't see its parent's methods, I don't understand how it can have a duty to obey their definitions.
I personally think that the bug being marked as bogus without any explanation of why it wasn't a real flaw (since it's non-obvious and I couldn't find any mention of it in the documentation) is a bit wrong, but yeah. That aside, I'm of the opinion line 2669 in zend_compile.c should actually read as follows:
} else if (child->prototype &&
(EG(error_reporting) & E_STRICT || EG(user_error_handler))) {
...which would avoid the error popping up when the parent's method was marked private. Given that you always have the option not logging E_STRICT though, and it doesn't really negatively impact anything, I suppose it's not really a big deal. I definitely don't see how it could have been intentional, but I'm not a PHP engine developer either.
I think there are two possibilities here. Either it's a bug or the documentation on PHP.net/manual is incorrect. Here are three sections of the PHP manual. First on inheritance:
Object Inheritance
Inheritance is a well-established
programming principle, and PHP makes
use of this principle in its object
model. This principle will affect the
way many classes and objects relate to
one another.
For example, when you extend a class,
the subclass inherits all of the
public and protected methods from the
parent class. Unless a class overrides
those methods, they will retain their
original functionality.
This is useful for defining and
abstracting functionality, and permits
the implementation of additional
functionality in similar objects
without the need to reimplement all of
the shared functionality.
And on abstract classes:
Class Abstraction
PHP 5 introduces abstract classes and methods. It is not allowed to create an instance
of a class that has been defined as abstract. Any class that contains at least one
abstract method must also be abstract. Methods defined as abstract simply declare the
method's signature they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's
class declaration must be defined by the child; additionally, these methods must be
defined with the same (or a less restricted) visibility. For example, if the abstract
method is defined as protected, the function implementation must be defined as either
protected or public, but not private.
Finally, interfaces
Object Interfaces
Object interfaces allow you to create code which specifies which methods a class must
implement, without having to define how these methods are handled.
Interfaces are defined using the interface keyword, in the same way as a standard class,
but without any of the methods having their contents defined.
All methods declared in an interface must be public, this is the nature of an interface.
Suffice it to say: there is nothing in the documentation that mentions inheritance of private methods. If there is a relationship between the parent and child method signatures, then it is not documented and the bug report should at least show someone that the documentation needs to be updated (if the decision to have this behavior is intentional). And if there was not supposed to be a relationship, then well, it's a real bug.
That's my opinion...
In the bug report when you remove the interface there isn't an error.
That makes it "more" strange behavior because the interface is just empty.
I guess this is a design decision of the language. The Java language developers decided that this should be possible.
Private methods should certainly not be ignored by inheritance, consider for example Template method pattern where you may override behavior of a function in a derived class, but the parent class can still call that function
public class Parent {
public final function doThings() {
$this->initialize();
$this->customStuff();
$this->cleanup();
}
private final function initialize() {
// initialize processing
}
private final function cleanup() {
// cleanup processing
}
private function customStuff() {
// parent specific processing
}
}
public class Derived extends Parent {
private function customStuff() {
parent::customStuff();
// + derived class specific processing
}
}
Calling doThings method on Derived class instance will do parent specific processing, but because of possibility to override private methods it is still possible to take advantage of the extension point provided by the non-final parent class customStuff method.
EDIT: Also PHP method signature consists of only the method name as you can define a method taking zero parameters and still call it with multiple parameters. Function can then access the arguments using func_get_args function.