workaround for multiple inheritences in PHP?

0 votes
asked Aug 17, 2010 by user151841

In a lot of my PHP classes, I have this code:

private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;


public function isError() {
    return $this->blnError;
}

public function getErrorCode() {
    return $this->intErrorCode;
}

private function setError( $strError, $intErrorCode = NULL ) {
    $this->blnError = TRUE;
    $this->intErrorCode = $intErrorCode;
    $this->strError = $strError;
}

The point is so that outside code can know if an object has an error state, what the string of the error is, etc. But to have this exact code in a bunch of different classes is repetitious!

I'd love to have a dual-extension where I could do

class childClass extends parentClass, error {
    ...
}

And have those properties and methods inborn, But PHP doesn't support multiple inheritances. What I'm thinking about doing is creating an error class that exists inside each class. If I make it public, I can call it directly through the object

if ( $myObject->error->isError() ) {...}

but wouldn't that also make its error status settable from outside the containing class,

$myObject->error->setError("I shouldn't be doing this here");

which I would rather avoid?

Or I could write 'gateway' functions in the containing class, which do the appropriate calls on the error object, and prevent setting the error status from outside,

class childClass extends parentClass {

    private $error;

    public function __construct(...) {
        ...
        $error = & new error();
        ...
    }

    public function isError() {...}
    public function getError() {...}
    public function getErrorCode() {...}
    private function setError() {...}

    ...
}

but that leads to (some of) the code duplication that I'm trying to avoid.

What's the optimal solution here? I'm trying to have functionality for error statuses for a number of objects, so that the outside world can see their error state, with minimal repetition.

3 Answers

0 votes
answered Aug 17, 2010 by ed-mazur

Use composition instead of inheritance.

class Errors {

    private $strError = "";
    private $intErrorCode = NULL;
    private $blnError = FALSE;


    public function isError() {
        return $this->blnError;
    }

    public function getErrorCode() {
        return $this->intErrorCode;
    }

    private function setError( $strError, $intErrorCode = NULL ) {
        $this->blnError = TRUE;
        $this->intErrorCode = $intErrorCode;
        $this->strError = $strError;
    }

}

And now use a private instance variable to refer to it:

class childClass extends parentClass {
    private $errors = new Errors();
    ...
}

The private visibility prevents you from referencing $errors outside of the class.

There's also no need to create isError(), getError(), etc. inside childClass (and therefore no need to worry about code duplication). Simply call $this->errors->isError(), $this->errors->getError(), etc. If you still wanted to require those methods to be implemented though, as suggested below, you could specify an interface.

0 votes
answered Aug 17, 2010 by ircmaxell

You could also abuse the __call magic method to do the same thing:

public function __call($name, array $arguments) {
    $name = strtolower($name);
    if (isset($this->methods[$name])) {
        array_unshift($arguments, $this);
        return call_user_func_array($this->methods[$name], $arguments);
    }
    throw new BadMethodCallException('Method does not exist');
}

Note that I said abuse... Ideally, I'd think of a different architecture rather than having all these "common methods" everywhere. Why not use an exception instead of checking $foo->isError? If that's not appropriate, why not decorate a class?

class Errors 
    protected $object = null;
    public function __construct($object) {
        $this->object = $object;
    }
    public function __call($method, array $arguments) {
        $callback = array($this->object, $method);
        if (is_callable($callback)) {
                return call_user_func_array($callback, $arguments);
        }
        throw new BadMethodCallException('Method does not exist');
    }
    public function __get($name) { return $this->object->$name; }
    public function __set($name, $value) { $this->object->$name = $value; }
    //  Your methods here
    public function isInstance($name) { return $this->object instanceof $name; }
}

Then just "wrap" your existing object in that class:

$obj = new Errors($obj);
$obj->foo();
0 votes
answered Sep 15, 2017 by collin-krawll

As of PHP 5.4, you can use Traits.

For example you could make Trait called ErrorTrait like this:

trait ErrorTrait {
    private $strError = "";
    private $intErrorCode = NULL;
    private $blnError = FALSE;


    public function isError() {
        return $this->blnError;
    }

    public function getErrorCode() {
        return $this->intErrorCode;
    }

    private function setError( $strError, $intErrorCode = NULL ) {
        $this->blnError = TRUE;
        $this->intErrorCode = $intErrorCode;
        $this->strError = $strError;
    }
}

Then you would define your child class like this:

class childClass extends parentClass {
    use ErrorTrait;

    ...
}

Traits work basically like copy/paste so all of the code in the trait would be available within the class (without the code duplication).

Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...