PHP5 10/10/2003 CVS.
Currently, I don't see an easy way of accessing the constants of a child
class from a base class. Consider the following basic example:
abstract class myBaseClass {
function getXML() {
$doc = new domDocument();
$node = $doc->createElement(child::ElementName);
$doc->appendChild($node);
return $doc->saveXML($node);
}
}
class myChildClass extends myBaseClass {
const ElementName = 'foo';
// ..lots of methods, etc here..
}
$foo = new myChildClass();
print($foo->getXML());
Of course, this does not work as there is no 'child::' accessor. The
alternatives
to making this work are:
Use a protected variable in the child (and, of course, don't forget to
also say
'protected $ElementName;' in the base class) and use $this->ElementName
for accessing the child's ElementName from the parent. This is probably good
enough, but much less than ideal.
Use this beautifully low maintenance, high performance piece of code in the
base class (not):
$className = get_class($this);
switch($className) {
case 'mychildclass':
$childElementName = myChildClass::ElementName;
break;
case 'myotherchildclass':
$childElementName = myOtherChildClass::ElementName;
break;
// etc.....
}
.... ick!
You may be able to shortcut the need for the switch statement with some
variable variable and/or eval() trickery, but let's not even go there..
So are there any plans on implementing child:: ? Is there a way of accessing
child constants without having to explicitly known the current and/or
child class
name?
Any input appreciated-
Dan Cox
This sounds like you're doing something wrong (no offense!).
You want to access a constant of a descendant class, when
your ancestor doesn't even know if it exists.
Well, that sounds more than a little odd (backwards even).
Why not just use a property with a known name, and set the
value of that property in you descendant class constructor?
Performance wise, its not going to make much difference,
because no matter what you are doing, to dynamically resolve
the value of a constant will involve hash lookups.
The other alternative, and this is the official POV of the
Zend guys IIRC, is that you can use eval() to look up
the value:
$node = $doc->createElement(eval(get_class($this) . "::ElementName"));
If you think about it, what exactly does child:: refer to anyway?
A child class of the current object? But which one? What if
the child doesn't have the constant? What if there are interfaces
involved?
The engine doesn't know about descendant classes either (the
inheritance tree works in the other direction), and it shouldn't
have to second-guess what your code is doing - its much clearer to
explicitly write code like that eval above.
Hope that helps!
--Wez.
----- Original Message -----
From: "Dan Cox" dan@wep.net
To: internals@lists.php.net
Sent: Friday, October 10, 2003 8:10 AM
Subject: [PHP-DEV] Accessing child constants from base class
PHP5 10/10/2003 CVS.
Currently, I don't see an easy way of accessing the constants of a child
class from a base class. Consider the following basic example:abstract class myBaseClass {
function getXML() {
$doc = new domDocument();
$node = $doc->createElement(child::ElementName);
$doc->appendChild($node);
return $doc->saveXML($node);
}
}class myChildClass extends myBaseClass {
const ElementName = 'foo';
// ..lots of methods, etc here..
}$foo = new myChildClass();
print($foo->getXML());Of course, this does not work as there is no 'child::' accessor. The
alternatives
to making this work are:Use a protected variable in the child (and, of course, don't forget to
also say
'protected $ElementName;' in the base class) and use $this->ElementName
for accessing the child's ElementName from the parent. This is probably
good
enough, but much less than ideal.Use this beautifully low maintenance, high performance piece of code in
the
base class (not):$className = get_class($this);
switch($className) {
case 'mychildclass':
$childElementName = myChildClass::ElementName;
break;
case 'myotherchildclass':
$childElementName = myOtherChildClass::ElementName;
break;
// etc.....
}
.... ick!You may be able to shortcut the need for the switch statement with some
variable variable and/or eval() trickery, but let's not even go there..So are there any plans on implementing child:: ? Is there a way of
accessing
child constants without having to explicitly known the current and/or
child class
name?Any input appreciated-
Dan Cox
How about an abstract method in the base class called getElementName(),
which each child will implement to return its element name.
--
Ard
I am running into a complex PHP problem, where PHP, different versions I
tried, under redhat or mandrake, dies with sig fault in a way that I
could not clearly reproduce... there are different components involved
and I need help narrowing this down....
Is this the right list?
Thanks alot.
Mohamed~
Ard Biesheuvel wrote:
How about an abstract method in the base class called
getElementName(), which each child will implement to return its
element name.
Hi Ard-
This wouldn't work because the abstract method in the base class is
still running in the 'base class scope' and can't see the child's
constants.
Dan Cox
This works fine for me:
abstract class Base {
abstract function getElementName();
function getName()
{
return $this->getElementName();
}
}
class Derived extends Base {
const ElementName = 'DerivedElementName';
function getElementName()
{
return ElementName;
}
}
$c = new Derived();
echo $c->getName();
--
Ard
Hi Ard-
Sorry, I must of misunderstood you. Yes, that would work
fine. Now copy/paste the getElementName() function in the
derived class to all 100's of other derived classes and wonder
why you couldn't just do this with one base class function. :)
Dan Cox
Ard Biesheuvel wrote:
This works fine for me:
abstract class Base {
abstract function getElementName(); function getName() { return $this->getElementName(); }
}
class Derived extends Base {
const ElementName = 'DerivedElementName'; function getElementName() { return ElementName; }
}
$c = new Derived();
echo $c->getName();
Hi Wez-
Wez Furlong wrote:
This sounds like you're doing something wrong (no offense!).
You want to access a constant of a descendant class, when
your ancestor doesn't even know if it exists.
Well, that sounds more than a little odd (backwards even).
Why not just use a property with a known name, and set the
value of that property in you descendant class constructor?
Yes, this is how I'm doing things now. It just seems like I
shouldn't be forced to always have to use
parent::__construct(MY_CONSTANT)
Performance wise, its not going to make much difference,
because no matter what you are doing, to dynamically resolve
the value of a constant will involve hash lookups.The other alternative, and this is the official POV of the
Zend guys IIRC, is that you can use eval() to look up
the value:$node = $doc->createElement(eval(get_class($this) . "::ElementName"));
Yes. Normally, at least with most other programming languages,
using eval() is a major performance hit (as much as 10x slower),
so it shouldn't be used unless there is absolutely no other way.
Maybe this isn't the case with PHP?
If you think about it, what exactly does child:: refer to anyway?
A child class of the current object? But which one? What if
the child doesn't have the constant? What if there are interfaces
involved?
A deriving class of the current instance .. so childInstance::
then :) Perhaps you could use interfaces to enforce children
having the needed constants.
The engine doesn't know about descendant classes either (the
inheritance tree works in the other direction), and it shouldn't
have to second-guess what your code is doing - its much clearer to
explicitly write code like that eval above.Hope that helps!
--Wez.
I suppose. It just seems odd that a class can talk to it(self::)
and it's parent:: but not its child:: even though the child::
instance started the conversation in the first place :)
Dan Cox
Performance wise, its not going to make much difference,
because no matter what you are doing, to dynamically resolve
the value of a constant will involve hash lookups.The other alternative, and this is the official POV of the
Zend guys IIRC, is that you can use eval() to look up
the value:$node = $doc->createElement(eval(get_class($this) . "::ElementName"));
Yes. Normally, at least with most other programming languages,
using eval() is a major performance hit (as much as 10x slower),
so it shouldn't be used unless there is absolutely no other way.
Maybe this isn't the case with PHP?
It'll be slow no matter what you do, because you need to dynamically
reference the constant value. You could also use the switch construct
you already posted, or use the solution suggested by Ard; all of these
are slow, particularly switch when used with a large number of string
'case's. You might actually find that the eval works out faster.
If you're really thinking of writing high performance code in PHP,
you shouldn't be writing code that has 100's of class definitions ;-)
I suppose. It just seems odd that a class can talk to it(self::)
and it's parent:: but not its child:: even though the child::
instance started the conversation in the first place :)
The engine only stores child->parent relationships, not child<->parent
relationships. Think about it for a moment... can you do this kind
of thing in compiled languages? Do you know why you can't?
The reason is that the compiler has no way of knowing what classes are
going to extend it at the time it compiles the base class.
This is why I suggested that trying to dynamically access a constant
(eg: compile time!) of a child class just seems wrong.
--Wez.
Wez-
Wez Furlong wrote:
Performance wise, its not going to make much difference,
because no matter what you are doing, to dynamically resolve
the value of a constant will involve hash lookups.The other alternative, and this is the official POV of the
Zend guys IIRC, is that you can use eval() to look up
the value:$node = $doc->createElement(eval(get_class($this) . "::ElementName"));
Yes. Normally, at least with most other programming languages,
using eval() is a major performance hit (as much as 10x slower),
so it shouldn't be used unless there is absolutely no other way.
Maybe this isn't the case with PHP?It'll be slow no matter what you do, because you need to dynamically
reference the constant value. You could also use the switch construct
you already posted, or use the solution suggested by Ard; all of these
are slow, particularly switch when used with a large number of string
'case's. You might actually find that the eval works out faster.If you're really thinking of writing high performance code in PHP,
you shouldn't be writing code that has 100's of class definitions ;-)
hehe. Normally, I'd agree with 100's of class definitions being a
bad idea, but my particular case warrants it. Basically, I'm using
PHP's new DOM features to create an API to easily build XML
document fragments. It seems to make sense that each derived
class be responsible for building only the bit of XML it should
know about. All of these derived classes then extend an abstract
class which provides the children with getXML() functionality. The
code using the API then uses the objects to build a full XML
document (based on a particular XML Schema). Another reason
for using the derived classes is for enforcing data types, etc. at
the application level, which allows me to throw informative
Schema_Exception() type errors. The new dom->validate()
functionality is nice, but not so useful when building an XML
document yourself. AFAIK it also only works with DTDs and not
XML Schemas (the XML Schema support for libxml2 is... quite
lacking at this time).
Of course, I'm also using PHP's new __autoload() feature to
keep performance up. :)
On a side note, when an exception is thrown from inside the
__autoload() function, PHP only reports 'exception thrown in
__autoload' and no other information about the exception. Is
this a bug?
suppose. It just seems odd that a class can talk to it(self::)
and it's parent:: but not its child:: even though the child::
instance started the conversation in the first place :)The engine only stores child->parent relationships, not child<->parent
relationships. Think about it for a moment... can you do this kind
of thing in compiled languages? Do you know why you can't?
The reason is that the compiler has no way of knowing what classes are
going to extend it at the time it compiles the base class.This is why I suggested that trying to dynamically access a constant
(eg: compile time!) of a child class just seems wrong.--Wez.
I understand. For some reason, I just thought that self:: and
parent:: were evaluated at runtime instead of compile time.
I believe I will just stick with a protected variable in the derived
classes instead of a constant. This seems the easiest (and safe
enough in my case) approach.
Thanks-
Dan Cox