Extend Class with Final Constructor on PHP
I want extend class which have final constructor (in my case it's SimpleXMLElement), but i have problems because when i use:
class myclass extends Simp开发者_StackOverflowleXMLElement {
function __construct($xmlVersion='1.0', $xmlEncoding='ISO-8859-1', $rootName='root'){
parent::__construct("<?xml version='$xmlVersion' encoding='$xmlEncoding'?><$rootName />");
}
I get error:
Fatal error: Cannot override final method SimpleXMLElement::__construct()
When i delete constructor i get this error:
Fatal error: Uncaught exception 'Exception' with message 'SimpleXMLElement::__construct() expects at least 1 parameter, 0 given'
I miss something or doesn't understand how properly call parent constructor which is final. I don't want override methods just expand class, but i can't expand because it required __construct(). So i missed something and back where started.
Can somebody explain where i was wrong?
I just went through this exact thing. You don't need to extend it. Make a class that holds SimpleXMLElement objects. I believe this is what Nikola meant.
class XmlResultSet
{
public $xmlObjs = array();
public function __construct(array $xmlFiles)
{
foreach ($xmlFiles as $file) {
$this->xmlObjs[] = new XmlResult($file);
}
}
}
class XmlResult
{
private $xmlObj;
public function __construct($file)
{
try {
$this->xmlObj = new SimpleXMLElement($file, 0, true);
}
catch (Exception $e) {
throw new MyException("Invalid argument ($this)($file)(" . $e .
")", PHP_ERRORS);
}
}
public function otherFunctions()
{
return $this->xmlObj->movie['name']; // whatever
}
}
I would use Delegate wrapper design in this case. You should consider composition instead of inheritance here.
class myclass extends SimpleXMLElement {
public static function getInstance($xmlversion = '1.0', $xmlencoding = 'ISO-8859-1', $rootName='root') {
return new self("<?xml version='$xmlVersion' encoding='$xmlEncoding'?><$rootName />");
}
}
Well, final
means final. No overriding the method. Even if you ask nicely. I suggest adding a static make() method to your new class. Something like:
class myclass extends SimpleXMLElement {
static function make($data, $xmlVersion='1.0', $xmlEncoding='ISO-8859-1', $rootName='root'){
$obj=parent::__construct($data);
$obj->x=$xmlVersion;
$obj->e=$xmlEncoding;
$obj->r=$rootName;
return $obj;
}
}
There can be valid reasons to extend a third-party "final" class, resulting in more readable/maintainable code than completely duplicating it or creating some convoluted work-around. And certainly preferable over changing the third-party source code and removing the "final" keyword.
PHP supplies the extension "Componere" to accomplish such a feat in those rare cases where it truly is the best option. Below an example that shows how a the child class can be defined as a "trait", which can then be used to dynamically extend a final parent class:
<?php
declare(strict_types=1);
/*
* Final class would normally prevent extending.
*/
final class ParentC
{
public $parentvar;
public $secondvar;
function __construct() { echo( "\r\n<br/>".$this->parentvar = 'set by '.get_class().'->parentconstruct' ); }
function parentf() { echo( "\r\n<br/>".get_class().'->parentf >> '.$this->parentvar ); }
}
/*
* Extended class members.
*/
trait ChildC /* extends ParentC */
{
function __construct() {
// Call parent constructor.
parent::__construct();
// Access an inherited property set by parent constructor.
echo( "\r\n<br/>".get_class().'->overridden_constructor >> '.$this->parentvar );
}
function parentf() {
// Call overridden parent method.
parent::parentf();
// Access an inherited property set by constructor.
echo( "\r\n<br/>".get_class().'->overridden_parentf >> '.$this->parentvar );
}
function dynamicf( $parm = null ) {
// Populate a parent class property.
$this->secondvar = empty( $parm ) ? 'set by '.get_class().'->dynamicf' : $parm;
// Access an inherited property set by parent constructor.
echo( "\r\n<br/>".get_class().'->dynamicf >> '.$this->parentvar );
}
}
/*
* Register the dynamic child class "ChildC", which is
* derived by extending "ParentC" with members supplied as "ChildC" trait.
*/
$definition = new \Componere\Definition( 'ChildC', ParentC::class );
$definition->addTrait( 'ChildC' );
$definition->register();
/*
* Instantiate the dynamic child class,
* and access its own and inherited members.
*/
$dyno = new ChildC;
$dyno->parentf();
$dyno->dynamicf( 'myvalue ');
// Our object is also recognized as instance of parent!
var_dump( $dyno instanceof ChildC, $dyno instanceof ParentC, is_a( $dyno, 'ParentC') );
var_dump( $dyno );
?>
I know this is an old post but I had a similar problem just now. I was actually including the same class file twice. Use include_once() or require_once() instead of include() or require().
精彩评论