I have an abstract class I am inheriting from:
abstract class Test
{
public function GetTests()
{
}
}
and I have a concrete that I have been using the abstract classes implementation for most of the time:
class Concrete extends Test
{
// No problemmos
}
I recently had to implement a different version of the GetTests method, and in fact I wanted to overwrite it as it's built into all of my routing:
class Concrete extends Test
{
public function GetTests( $newArgument )
{
// notice $newArgument
}
}
However I get this error message:
Declaration of Concrete::GetTests() should be compatible with Test::GetTests()
Apart from copying the entirety of the functions from the abstract class for this concrete, even though I only need to implement this one method differently... Is there a way of getting around this?
I do understand that I could have:
abstract class Test
{
abstract public function GetTests();
}
But this is why I am snookered, because I no longer have the ability to modify how the underlying Test class is implemented... doh!... Unless I really have to..
Thanks to all great answers...
I have decided to de snooker myself (it's going to hurt but it's going to be worth it) and I will instantiate the Test class inside the Concrete class, implement concrete versions of all the Test class methods, and then inside them just call the instantiated Test class... This means in the future (or indeed now) I can simply not call that feature...
For context:
/* no longer abstract */ class UnitOfWorkController
{
public function GetUnits()
{
// Implementation
return View::make(...);
}
}
and...
class SomethingController /* no longer extends the UnitOfWorkController */
{
private $unitOfWorkController;
public function __Construct()
{
$this->unitOfWorkController = new UnitOfWorkController();
}
public function GetUnits()
{
return $this->unitOfWorkController->GetUnits();
// or I could just implement my own junk
}
}
Your concrete subclass is in violation of the Liskov Substitution Principle, which to cut a long story short says that if an object of class X can be processed by a given piece of code, then every possible subclass of X must also be able to be processed by the same piece of code.
Say I wanted to make another subclass of Test and wanted to implement my own GetTests method. The base class method doesn't accept any arguments at all, so that suggests that, if my subclass is to be substitutable for its superclass, my implementation of that method cannot take any arguments either. If I give my implementation arguments, then it no longer conforms to the specification as laid down by the superclass.
If I have code that does:
$object = new Test;
$test -> GetTests ();
then I can't substitute my subclass of Test without also changing the calling code to pass in an argument. Likewise if I do change it, then I have another subclass of Test that doesn't require an argument for GetTests then the code would have to change again. In fact the same code simply can't be used as is with both subclasses without having to jump through some hoops to determine the actual class and using the appropriate calling convention which means needing to know things about the class I'm about to use that I shouldn't need to know.
PHP is less strict than most OO languages about subclass method signitures matching their superclass, but it will issue a warning if they don't match. The only way to fix the warning is to have all subclasses have the same method signatures as the superclass they inherit from.
Child methods must have the same signature as the same method in a parent class. This includes required parameters and their typecasting.
For example, a child class of the following method must also have one parameter, and the parameter must cast to the ArgumentType class or a child of thereof.
public function something(ArgumentType $Argument)
{
}
You can, however, make the parameter optional by setting it to null or any other value:
public function something(ArgumentType $Argument = null)
{
}
In this case, child methods may omit this parameter.
From the PHP docs, see http://php.net/manual/en/language.oop5.abstract.php:
[…] Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. For example, if the child class defines an optional argument, where the abstract method's signature does not, there is no conflict in the signature.
The method signature of Concrete::GetTests() has a variable while Test::GetTests() does not. Since you have already defined this method within Test, it is now being inherited. The inherited version is not compatible with your overridden version.
Here are your options:
Add $newArgument to the parameters list in Test::GetTests().
Remove $newArgument from the parameters list in Concrete::GetTests().
Rename Concrete::GetTests() to something else.
PHP does not support this, as the error message says. If you want to override the function, it has to have the same footprint, which in your case it doesn't
What you could do is use a magic method: http://php.net/manual/en/language.oop5.overloading.php#object.call
the parameter array is a separate entity, so you 'decide' in your code (which you can override) what to do with which parameter.
I wanted to link a blog I read about this, but couldn't find the one I was thinking of. There is this rather strangely formatted one, not sure if it any good, but it does touch on some of the issues.
You could obviously add the argument to the parent, but this is 'leaking' upwards. If other childeren want even more, you'd get a big party of random paramteres that all can be nulled.
Related
I have a general problem with this use case: I have a class A. This class has a non-abstract method doStuffCallback() which could be overridden but it's not necessary for every subclass. But: I want to ensure that if the method is overriden the subclass-method must call the parents method.
Example:
abstract class A {
private function doStuff() {
$this->doStuffCallback();
}
protected function doStuffCallback() {
// IMPORTANT CODE HERE
}
}
class B extends A {
protected function doStuffCallback() {
parent::doStuffCallback(); // I want to enforce this because the parents method code is important
// ALSO IMPORTANT CODE
}
}
Because the overridden method does the same thing it would be very ugly to define two methods for the same responsibility and a private helper-method which calls both. Like this:
abstract class A {
private function doStuff() {
$this->callDoStuffCallback();
}
private function callDoStuffCallback() {
$this->internalDoStuffCallback();
$this->doStuffCallback();
// This is VERY ugly
}
private function internalDoStuffCallback() {
// IMPORTANT CODE HERE
}
protected function doStuffCallback() {}
}
class B extends A {
protected function doStuffCallback() {
// IMPORTANT CODE
}
}
This is really ugly and laborious. So my question: Is there a way in PHP to force overriden methods to call the parents method?
No. There is no such language feature in PHP; this restriction is not possible in most subtype-'OO' languages.
Instead programs must rely on explicit documentation contracts; and hopefully, unit testing to ensure conformance.
Guards may also be employed such that, at some point by and by when a method on the parent class is used, it could throw an exception if the 'current state' is not valid (eg. such and such a method has not been called yet). This may also be made more explicit by making the subclass required to call (as defined in the documentation contract) some special method, instead of simply the overriden super method. However, such is outside of any type system.
While the self:: scope could be used (eg. call non-overriden method which calls overriden method), this would involve further magic (eg. some stack state) to avoid infinite recursion loops; and it would be as easy to accidentally omit usage.
My recommendation is to call a (private) method that calls this 'maybe overriden' method in relationship to whatever logic applies, as shown in the example (although hopefully with more task specific tames). Then the (protected) overriden method is not expected or required to handle any of the special logic itself; nor is it meant to be called directly outside of the context established by the parent class - it is just what it currently claims to be, a special callback.
I tend to disagree with "This is VERY ugly". It is the standard way of handling this use case and a variant of the Template Method Pattern.
Now I am just guessing because you did not provide a real example but if you say that the two methods "do the same thing", there might be something wrong with your design. If they do the same thing, why is calling the parent implementation necessary if the subclass does the same thing in a different way? To me it sounds like the method actually does more than one thing and you might be able to break it down into several parts that can be overridden individually (or not, then make them private or final).
I know this is an old topic but I was asking myself the same question and what I did is :
abstract class A {
private function doStuff() {
$this->doStuffCallback();
}
final protected function doStuffCallback() {
// IMPORTANT CODE HERE
$this->callNewFunction();
}
abstract protected function callNewFunction();
}
class B extends A {
protected function callNewFunction() {
// ALSO IMPORTANT CODE
}
}
So basically I would mark as "final" the function you wish to force the code for every child and then call a new "Abstract" function to force the childs to implement it. If you do not wish to force the new "Abstract" function, simply don't make it abstract.
Edit : This is basically #Fabian Schmengler's answer but more concrete with your example.
No, you can access, you can use method for parent, like this
<?php
class A {
function s1($p1) {
echo 's1: '.$p1;
}
}
class B extends A {
public function callParent($method, $p1) {
parent::$method($p1);
}
}
$b = new B();
$b->callParent('s1', 'param1');
or replace extending on magic methods __call and etc. https://github.com/StagnantIce/php_extend_magic/blob/master/AExtendClass.php
I am trying to understand inheritance in php. I have two classes ShopProduct and CdProduct. ShopProduct class have one method getName. CdProduct class inherit (extends) ShopProduct class and have method DoWork. In index.php file I create a one CdProduct object and pass it to the method that takes one parameter like this function Work(ShopProduct $item){...}. Now, I can understand that I can call getName method using $item parameter, and I know that I can call DoWork method using $item, but I can't understand how this is possible... I hope I was clear... :-)
It's possible because the object passed as parameter happens to contain the DoWork method. It's risky calling this sort of methods (belonging to the inheriting class) without type-checking.
Why does it let you give a CdProduct parameter when the mentioned one is ShopProduct? Well any class derived from the ShopProduct will contain all its attributes and methods, thus not affecting functionality in the method body.
What can be achieved through this is having a Work method that can receive as a parameter any type derived from ShopProduct(CdProduct, BookProduct, CandyProduct) without needing a separate function for each type [WorkCd(CdProduct $item) or WorkBook(BookProduct $item)] that actually does the same thing.
If you really need access to derived-type specific methods and attributes you can do something like a switch on object class name:
switch(get_class($item))
{
case "CdProduct":
//CdProduct object specific code here
break;
case "BookProduct":
//BookProduct object specific here
break;
//And so and so forth...
}
The reason why it works is because PHP 5 introduces type hinting: http://php.net/manual/en/language.oop5.typehinting.php .
So indeed CdProduct inherits all the public and protected methods and attributed declared in ShopProduct and thus you can call the getName() method. When you pass the CdProduct instance to the Work function which declares the first parameter to be ShopProduct (and not CdProduct) you are forcing the parameter to be casted to ShopProduct.
So if you pass as parameter something different from CdProduct instance, like a string as an example, you have an error, but because of the inheritance this doesn't fail (it should however print a notice).
As you can read from the comments in the link posted above:
p.s. do note: when used in inherited classes a STRICT-notice comes up, because the function definition of the inherited class doesn't match the parent's definition (different type hinting) - but it works great!
I know I cannot overload methods in PHP. And, as far as I know, private methods in a class are invisible to classes that extend the base class. So why this does not work?
class Base {
private function foo($arg) {
print "Base $arg";
}
}
class Child extends Base {
public function foo() {
print "Child";
}
}
$c = new Child;
print $c->foo();
The error:
PHP Strict Standards: Declaration of Child::foo() should be compatible with Base::foo($arg) in /var/www/boludo.php on line 17
I assumed that foo($arg) method is invisible in Child class because is private. So, I'm not overloading foo, I'm just creating a method called foo.
To fix the Notice, simply change foo() in the Child to
public function foo($arg = null) {
As to the question "why does this not work":
Visibility in PHP is strictly about runtime access. It doesn't affect how you can extend/compose/overload classes and methods. Loosening the visibility of a private method from a Supertype in a Subtype will add a separate method in the subtype with no access to the same named method in the supertype. However, PHP will assume a parent-child relationship for these. That didn't cause the Notice though. At least, not on it's own.
The reason why you get the Notice, is that you are then also trying to change the method signature. Your foo() does no longer require $arg to be passed to it. When you assume a parent-child relationship between the methods, this is a problem because 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" without breaking the program. In other words: if you have code that uses Base, you should be able to replace Base with Child and the program should still work as if it was using Base.
Assume your Base also has a public method bar().
class SomeClientUsingBase
{
public function doSomethingWithBase(Base $base)
{
$result = $base->bar();
// …
Now imagine Child changes bar() to require an argument. If you then pass Child for Base into the client, you will break the client, because the client calls $base->bar(); without an argument.
Obviously, you could change the client to pass an argument, but then the code really depends on how Child defined the method, so the Typehint is wrong. In fact, Child is not a Base then, because it doesn't behave like a Base. It's broken inheritance then.
Now the funny thing is, if you remove that $arg from foo(), you are technically not violating LSP, because the client would still work. The Notice is wrong here. Calling $base->foo(42) in a client that previously used Base will still work with a Child because the Child can simply ignore the argument. But PHP wants you to make the argument optional then.
Note that LSP also applies to what a method may return. PHP just doesn't include the return type in the signature, so you have take that into account yourself. Your methods have to return what the Supertype returned or something that is behaviorally equivalent.
You can do function overloading in PHP using __call function: http://www.php.net/manual/en/language.oop5.overloading.php#object.call
Apart from that, your problem is that in that way, you violate the Substitutability principle:
http://en.wikipedia.org/wiki/Liskov_substitution_principle
Something that PHP uses. In that way, if you replace an object of Base class type to one with a Child class type, Substitutability is violated. You are changing the interface of the base class in the derived one, removing the argument of method foo(...) and in this way, objects of Base class type can not be replaced with objects of Child class type without breaking the program, thus violating Liskov's Substitutability Principle (LSP).
I'm extending a class, but in some scenarios I'm overriding a method. Sometimes in 2 parameters, sometimes in 3, sometimes without parameters.
Unfortunately I'm getting a PHP warning.
My minimum verifiable example:
http://pastebin.com/6MqUX9Ui
<?php
class first {
public function something($param1) {
return 'first-'.$param1;
}
}
class second extends first {
public function something($param1, $param2) {
return 'second params=('.$param1.','.$param2.')';
}
}
// Strict standards: Declaration of second::something() should be compatible with that of first::something() in /home/szymon/webs/wildcard/www/source/public/override.php on line 13
$myClass = new Second();
var_dump( $myClass->something(123,456) );
I'm getting PHP error/warning/info:
How can I prevent errors like this?
you can redefine methods easily adding new arguments, it's only needs that the new arguments are optional (have a default value in your signature). See below:
class Parent
{
protected function test($var1) {
echo($var1);
}
}
class Child extends Parent
{
protected function test($var1, $var2 = null) {
echo($var1);
echo($var1);
}
}
For more detail, check out the link: http://php.net/manual/en/language.oop5.abstract.php
Another solution (a bit "dirtier") is to declare your methods with no argument at all, and in your methods to use the func_get_args() function to retrieve your arguments...
http://www.php.net/manual/en/function.func-get-args.php
As of PHP 8.1, there's a cool hack to override a class's method with extra number of required arguments. You should use the new new in initializers feature. But how?
We define a class having a constructor always throwing a ArgumentCountError, and make it the default value of every extra required parameter (an improved version of #jose.serapicos's answer). Simple and cool!
Now let's see it in action. First, we define RequiredParam:
final class RequiredParameter extends \ArgumentCountError
{
public function __construct()
{
// Nested hack
throw $this;
}
}
And then:
class Base
{
public function something(string $baseParam): string
{
return $baseParam;
}
}
class Derived extends Base
{
public function something(
string $baseParam,
string|RequiredParameter $extraParam = new RequiredParameter(),
): string {
return "$baseParam + $extraParam";
}
}
This way, no one can bypass the extra parameters, because RequiredParameter is declared as final. It works for interfaces as well.
How Good or Bad is This?
One advantage is that it's a little more flexible than setting default parameters as null, as you can pass the constructor of RequiredParameter an arbitrary list of parameters and probably build a custom error message.
Another advantage is that it's handled less manually, and thus being more safe. You may forget about handling a null value, but RequiredParameter class handles things for you.
One major disadvantage of this method is that it breaks the rules. First and foremost, you must ask yourself why you would need this, because it breaks polymorphism in most cases. Use it with caution.
However, there are valid use cases for this, like extending parent class's method with the same name (if you cannot modify the parent, otherwise I recommend you to use traits instead), and using the child class as standalone (i.e. without the help of parent class's type).
Another disadvantage is that it requires you to use union types for each parameter. While the following workaround is possible, but it requires you to create more classes, which may hurt understandability of your code, as well as having little impact on maintainability and performance (based on your conditions). BTW, no hack comes for free.
Eliminating the use of Union Type
You could extend from or implement RequiredParameter the compatible type of the actual parameter to be able to remove the need for union type:
class BaseRequiredParameter extends Base
{
public function __construct()
{
throw new \ArgumentCountError();
}
}
class Derived extends Base
{
public function something(
string $baseParam,
Base $extraParam = new BaseRequiredParameter()
): string {
return "$baseParam + {$extraParam->something()}";
}
}
It's also possible for strings, if you implement the Stringable interface (e.g. Throwable implements it by default). It doesn't work for some primitive types including bool, int, float, callable, array, etc., however, if you're interested, you're still able to use some alternatives like Closure or Traversable.
For making your life easier, you may want to define the constructor as a trait and use it (I'm aware of this answer, but in fact, this is a valid useful case for a constructor in a trait, at least IMO).
Your interface/abstract class or the most parent class, should cotantin the maximum number of params a method could recieve, you can declare them explicitely to NULL, so if they are not given, no error will occur i.e.
Class A{
public function smth($param1, $param2='', $param3='')
Class B extends A {
public function smth($param1, $param2, $param3='')
Class C extends B {
public function smth($param1, $param2, $param3);
In this case, using the method smth() as an object of 'A' you will be obligated to use only one param ($param1), but using the same method as object 'B' you will be oblgiated to use 2 params ($param1, $param2) and instanciating it from C you have to give all the params
Using PHPUnit 3.6 I'm trying to test the exec() method in the below controller class. This method does two things:
Determines the name of the method to call based on the object's existing properties, and ...
If the determined controller method is callable it is executed and if not the method throws an exception
The (simplified) source code looks like this:
abstract class CLIController extends Controller
{
/* irrelevant class details here */
public function exec()
{
$action = ! empty($this->opts->args[0])
? $this->opts->args[0]
: $this->default_action;
if ( ! $action || ! is_callable(array($this, $action))) {
$msg = 'Invalid controller action specified';
throw new LogicException($msg);
} else {
$this->$action(); // <---- trying to get code coverage on this line!
}
}
}
So my problem is ...
I can't figure out how to get coverage on this part of the code:
} else {
$this->$action();
}
because I'm not sure how to (or that it's even possible to) test the invocation of a method whose name is not known in the context of the abstract class. Again: the method to be called is declared in child classes. Normally I would just mock an abstract method but I can't in this case because the method doesn't exist yet -- it will be specified by a child class.
What might be the answer ...
??? It may be possible that this line doesn't even need to be covered because it essentially relies on PHP's ability to correctly invoke a callable class method. If I successfully test that exec() throws an exception when it's supposed to, I know that correct functioning of the line in question depends on PHP functioning correctly. Does this invalidate the need to test it in the first place ???
If there is some way to mock the abstract class and create a method with a known name to add to the mocked class this would solve my problem and is what I've been trying unsuccessfully to do so far.
I know I could create a child class with a known method name but I don't believe it's a good idea to create a concrete child class just to test an abstract parent.
It could be that I need to refactor. One thing I don't want to do is leave child classes to implement the exec() function on their own.
What I've tried ...
Use some of PHP's reflection capabilities to no avail -- this may perhaps be due to my own inexperience with reflection and not its inability to handle this case, though.
Going back and forth through the PHPUnit manual and API docs. Unfortunately, as awesome as PHPUnit is, I often find the API documentation a bit light.
I would really appreciate any guidance on how best to proceed here. Thanks in advance.
I disagree with your stipulation that "it's [not] a good idea to create a concrete child class just to test an abstract parent." I do this quite often when testing abstract classes and usually name the concrete subclass after the test to make it clear.
class CLIControllerTest extends PHPUnit_Framework_TestCase
{
public function testCallsActionMethod()
{
$controller = new CLIControllerTest_WithActionMethod(...);
// set $controller->opts->args[0] to 'action'
$controller->exec();
self::assertTrue($controller->called, 'Action method was called');
}
}
class CLIControllerTest_WithActionMethod extends CLIController
{
public $called = false;
public function action() {
$this->called = true;
}
}
The code to make this test happen is trivial and can be easily verified by inspection.
I'm curious, why use is_callable instead of method_exists to avoid creating the array? It's probably just personal preference, but I'm wondering if there are any semantic differences.