Declaration of Methods Should Be Compatible With Parent Methods in PHP

Declaration of Methods should be Compatible with Parent Methods in PHP

childClass::customMethod() has different arguments, or a different access level (public/private/protected) than parentClass::customMethod().

Declaration of Child method should be compatible with Parent method

The problem is that the function declaration for the parent and child are not compatible (like the error says)

public function method([$args]) is the function declaration.

In your case the parent method does not take any parameters but the child method does, so you get the error.

To solve this you should have the parent method accept the same parameters as the child method. You will probably want to provide some sensible default for the parameters.

//parent class
function default_action($form_id = null) {

As RiggsFolly points out, you may not have control over the parent class and have to make your child class conform. In that case you can use a class property to pass the form id to the method.

//Child class

class Child extends Parent{
public $form_id;

function setFormId($form_id){
$this->form_id = $form_id;

}
function default_action(){
//Work with $this->form_id
}

}

//In code
$form_id = 123;
$C = new Child();
$C->setFormId($form_id);
$C->default_action();

Declaration of child method must be compatible with parent method

You can return a BarX but since B extends A, it must return the same type but since BarX is Bar, you can do it like that:

class B extends A {
public function foo(): Bar {
$barX = new BarX;
//some ops
return $barX; //'BarX' object
}
}

Declaration of Child method should be compatible to Parent Method

As you can see from the error message, you overrided yii\base\Model getAttributes() method. common\models\Product is extended from yii\db\ActiveRecord and ActiveRecord is extended from yii\base\Model.

If you really want to override this method, list all parameters (see here), it's easier to do with help of IDE. And by the way this is PHP feature and has nothing to do with OS or Yii2.

If it is your custom method for another purposes, you need to rename it in order to resolve conflict.

Declaration of child method should be compatible with that of parent method in PHP 5.3.13

It seems that this is a strict error being reported by PHP.

The discussion follows here: Declaration of Methods should be Compatible with Parent Methods in PHP

For the resolution, you will need to use the same declaration for both methods.

class CustomCourse extends BaseCourse {
function toArray($platform=GOLF_PLATFORM) {
//do something
}
}

Alternatively, you can turn off strict error checking in your php.ini file.

Declaration should be compatible with PHP 7

Array is a type. You're looking for array() (or []), since you're assigning a default value. I.e. $args = Array should be $args = array(). For reference:
4.7.2/Walker_Category

Must declaration of the child class method be compatible with parent class method in PHP?

As per PHP manual:

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.

Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same.

So, the parent class has 0 arguments, the child has 2 required ones. That is the problem. But since you changed your code, as you say:

The child method has given all 3 arguments a default value.

You reduced the number of required arguments in the child class to 0. So it works now.

To recapitate. This is wrong:

abstract class MyAbstractClass
{
abstract public function _myFunction(); // No parameters required
}

class Foobar extends MyAbstractClass
{
public function _myFunction(A $a, A $b, A $c = null) // Two parameters required
{
echo "abc";
}
}

This would also be wrong:

abstract class MyAbstractClass
{
abstract public function _myFunction(A $a, A $b, A $c); // Three parameters required
}

class Foobar extends MyAbstractClass
{
public function _myFunction(A $a, A $b) // Two parameters required
{
echo "abc";
}
}

This is okay:

abstract class MyAbstractClass
{
abstract public function _myFunction(); // No parameters required
}

class Foobar extends MyAbstractClass
{
public function _myFunction(A $a = null, A $b = null, A $c = null) // No parameters required
{
echo "abc";
}
}

And this is also okay:

abstract class MyAbstractClass
{
abstract public function _myFunction(A $a, A $b, A $c);
}

class Foobar extends MyAbstractClass
{
public function _myFunction(A $a, A $b, A $c = null)
{
echo "abc";
}
}

PHP: Declaration of ... should be compatible with that of ...

Overriding constructors is a special case:

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.

You can not do that with other methods.

Silence Declaration ... should be compatible warnings in PHP 7

1. Workaround

Since it is not always possible to correct all the code you did not write, especially the legacy one...

if (PHP_MAJOR_VERSION >= 7) {
set_error_handler(function ($errno, $errstr) {
return strpos($errstr, 'Declaration of') === 0;
}, E_WARNING);
}

This error handler returns true for warnings beginning with Declaration of which basically tells PHP that a warning was taken care of. That's why PHP won't report this warning elsewhere.

Plus, this code will only run in PHP 7 or higher.


If you want this to happen only in regard to a specific codebase, then you could check if a file with an error belongs to that codebase or a library of interest:

if (PHP_MAJOR_VERSION >= 7) {
set_error_handler(function ($errno, $errstr, $file) {
return strpos($file, 'path/to/legacy/library') !== false &&
strpos($errstr, 'Declaration of') === 0;
}, E_WARNING);
}

2. Proper solution

As for actually fixing someone else's legacy code, there is a number of cases where this could be done between easy and manageable. In examples below class B is a subclass of A. Note that you do not necessarily will remove any LSP violations by following these examples.

  1. Some cases are pretty easy. If in a subclass there's a missing default argument, just add it and move on. E.g. in this case:

    Declaration of B::foo() should be compatible with A::foo($bar = null)

    You would do:

    - public function foo()
    + public function foo($bar = null)
  2. If you have additional constrains added in a subclass, remove them from the definition, while moving inside the function's body.

    Declaration of B::add(Baz $baz) should be compatible with A::add($n)

    You may want to use assertions or throw an exception depending on a severity.

    - public function add(Baz $baz)
    + public function add($baz)
    {
    + assert($baz instanceof Baz);

    If you see that the constraints are being used purely for documentation purposes, move them where they belong.

    - protected function setValue(Baz $baz)
    + /**
    + * @param Baz $baz
    + */
    + protected function setValue($baz)
    {
    + /** @var $baz Baz */
  3. If you subclass has less arguments than a superclass, and you could make them optional in the superclass, just add placeholders in the subclass. Given error string:

    Declaration of B::foo($param = '') should be compatible with A::foo($x = 40, $y = '')

    You would do:

    - public function foo($param = '')
    + public function foo($param = '', $_ = null)
  4. If you see some arguments made required in a subclass, take the matter in your hands.

    - protected function foo($bar)
    + protected function foo($bar = null)
    {
    + if (empty($bar['key'])) {
    + throw new Exception("Invalid argument");
    + }
  5. Sometimes it may be easier to alter the superclass method to exclude an optional argument altogether, falling back to func_get_args magic. Do not forget to document the missing argument.

      /**
    + * @param callable $bar
    */
    - public function getFoo($bar = false)
    + public function getFoo()
    {
    + if (func_num_args() && $bar = func_get_arg(0)) {
    + // go on with $bar

    Sure this can become very tedious if you have to remove more than one argument.

  6. Things get much more interesting if you have serious violations of substitution principle. If you do not have typed arguments, then it is easy. Just make all extra arguments optional, then check for their presence. Given error:

    Declaration of B::save($key, $value) should be compatible with A::save($foo = NULL)

    You would do:

    - public function save($key, $value)
    + public function save($key = null, $value = null)
    {
    + if (func_num_args() < 2) {
    + throw new Exception("Required argument missing");
    + }

    Note that we couldn't use func_get_args() here because it does not account for default (non-passed) arguments. We are left with only func_num_args().

  7. If you have a whole hierarchies of classes with a diverging interface, it may be easier diverge it even further. Rename a function with conflicting definition in every class. Then add a proxy function in a single intermediary parent for these classes:

    function save($arg = null) // conforms to the parent
    {
    $args = func_get_args();
    return $this->saveExtra(...$args); // diverged interface
    }

    This way LSP would still be violated, although without a warning, but you get to keep all type checks you have in subclasses.



Related Topics



Leave a reply



Submit