I've toying around with the SPL stuff to get an object to behave like
an array. After looking at a few examples I noticed that they are
using get_object_vars ($this) a lot.
This all seems well and good when all an object's properties are
public, but it hits a snag when you're using access protection
(protected/private keyword), namely it exposes internal state that's
supposed to be invisible to the outside world.
Is there an alternative to get_object_vars ($this) that only exposes
the public members?
ReflectionClass::getProperties is closer to what I want, as it allows
you to tailor what properties are returned on the basis of their
protection level. Unfortunately, I need to remain backwards
compatible with PHP 5.2.
> ReflectionClass::getProperties is closer to what I want, as it allows
> you to tailor what properties are returned on the basis of their
> protection level. Unfortunately, I need to remain backwards
> compatible with PHP 5.2.
Did you eventually mean 4.2? ReflectionClass::getProperties should be
available in 5.2.
Helmut
I did mean 5.2, but actually, you're right. I wrote some code several
months ago that needed to use reflection to work around the lack of
late static binding in 5.2, and I guess the memory of that mutated
into remembering it as having to work around the lack of reflection in
5.2 instead. Sorry about that.
> I did mean 5.2, but actually, you're right. I wrote some code several
> months ago that needed to use reflection to work around the lack of
> late static binding in 5.2, and I guess the memory of that mutated
> into remembering it as having to work around the lack of reflection in
> 5.2 instead. Sorry about that.
No need for excuses :-)!
Have been confused myself with a get_object_vars($this) and __sleep()
issue some time ago, when I tried something like the following:
class Object {
public function __sleep() {
return array_keys(get_object_vars($this));
}
}
class Implementation extends Object {
}
$impl = new Implementation();
serialize($impl);
:-)
Helmut
What's the expected and actual result of that? Not had time to try
it.
Seems the reflection route isn't the way to go either, unless I've
missed something the getProperties method returns the keys of each
property and the class they're defined in. What I need was the keys
and the values.
As my class implements ArrayIterator I can write a toArray method that
works but as it uses a foreach loop I'm not sure it's the most
efficient method of doing it. Using $this in this context only seems
to return the public members, I'll have to experiment more to be
certain of this though.
public function toArray ()
{
foreach ($this as $key => $val)
{
$ret [$key] = $val;
}
return ($ret);
}
A strange thing I've noticed is that the use of get_object_vars I've
seen in the example code I based this on is that it does seem to only
restrict itself to the public members when used to implement the
interfaces as shown, but the same function used in the toArray method
seems to dump everything out regardless of its protection level.
class CmsItem implements ArrayAccess, IteratorAggregate, Countable
{
static public
$db = 'qwer',
$user = 'asdf';
private
$iterator = NULL,
$parent = 'zxcv',
$children = array ();
public
$itm_id = 0,
$usr_id_create = 0,
$usr_id_modify = 0,
$itm_parent = 0,
$itm_root = 0,
$itm_sort = 0,
$itm_date_create = 0,
$itm_date_modify = 0,
$itm_system = false,
$itm_archived = false,
$itm_visible = false,
$itm_published = false,
$itm_title = '',
$itm_path = '',
$itm_summary = '',
$itm_notes = '',
$itm_keywords = '';
public function toArray ()
{
//return (get_object_vars ($this));
foreach ($this as $key => $val)
{
$ret [$key] = $val;
}
return ($ret);
}
/**
* ArrayAccess implementation
*/
/**
* Set a value given it's key e.g. $A['title'] = 'foo';
*
* @param mixed key (string or integer)
* @param mixed value
* @return void
*/
public function offsetSet ($key, $value)
{
if (array_key_exists ($key, get_object_vars ($this)))
{
$this -> {$key} = $value;
}
}
/**
* Return a value given it's key e.g. echo $A['title'];
* @param mixed key (string or integer)
* @return mixed value
*/
public function offsetGet ($key)
{
if (array_key_exists ($key, get_object_vars ($this)))
{
return $this->{$key};
}
}
/**
* Unset a value by it's key e.g. unset($A['title']);
* @param mixed key (string or integer)
* @return void
*/
public function offsetUnset ($key)
{
if (array_key_exists ($key,get_object_vars ($this)))
{
unset ($this -> {$key});
}
}
/**
* Check value exists, given it's key e.g. isset($A['title'])
* @param mixed key (string or integer)
* @return boolean
*/
public function offsetExists ($offset)
{
return array_key_exists ($offset, get_object_vars ($this));
}
/**
* Iterator implementation
*/
/**
* Returns an iterator for for this object, for use with foreach
* @return ArrayIterator
*/
public function getIterator ()
{
if (!$this -> iterator)
{
$this -> iterator = new ArrayIterator ($this);
}
return ($this -> iterator);
}
/**
* Countable implementation
*/
public function count ()
{
return (count ($this -> getIterator ()));
}
}
This code was derived from http://articles.sitepoint.com/article/php5-standard-library
I tried to create a basic implementation for serializing objects. But
(as you discovered) get_object_vars($this), called from Implementation
does not return private members, defined in Object. And on the other
side, private members of Implementation cannot be used with __sleep() in
general (which makes this function pretty useless IMHO).
> Seems the reflection route isn't the way to go either, unless I've
> missed something the getProperties method returns the keys of each
> property and the class they're defined in. What I need was the keys
> and the values.
AFAICS it might be possible to get the value by using
<http://www.php.net/manual/en/reflectionproperty.getvalue.php>.
But I just reread your original post. And it seems, I am still confused ;):
get_object_vars($this) should do the thing, you want: it returns all
properties of the object $this, that are accessible in the context. That
means:
$foo = new Foo();
get_object_vars($foo);
Returns only public properties of $foo. Because only public properties
are accessible in the context, where the function is called.
But:
class Foo() {
public function getProperties() {
return get_object_vars($this);
}
}
$foo = new Foo();
$foo->getProperties();
Returns all properties of $foo, also private, because in the context,
where get_object_vars() is called in this case, they are accessible.
But here another case (and that was my confusion):
class Foo() {
private $_propertyOfFoo;
public function getProperties() {
return get_object_vars($this);
}
}
class Bar extends Foo {
private $_propertyOfBar;
}
$bar = new Bar();
$bar->getProperties();
This will *not* return Bar::_propertyOfBar. Because getProperties() (and
get_object_vars($this)) is called from the context of Foo. And Foo has
no access to the private property Bar::_propertyOfBar, it doesn't even
know of it. So if you want all properties of an instance of Bar, you
have to overwrite getProperties() and must call Foo:getProperties() also:
class Foo() {
private $_propertyOfFoo;
public function getProperties() {
return get_object_vars($this);
}
}
class Bar extends Foo {
private $_propertyOfBar;
public function getProperties() {
return array_merge(parent::getProperties(), get_object_vars($this));
}
}
$bar = new Bar();
$bar->getProperties();
Helmut
> But I just reread your original post. And it seems, I am still confused ;):
And I'm still confused: Just saw, you want exactly the opposite, just
the public properties... So while all I wrote about get_object_vars() is
true, it doesn't meet your requirements. Sorry about that :(.
But ReflectionClass:getProperties() and ReflectionProperty::getValue()
should do this:
public function toArray() {
$ro = new ReflectionObject($this);
$publicReflectionProperties =
$ro->getProperties(ReflectionProperty::IS_PUBLIC);
$publicProperties = array();
foreach ($publicReflectionProperties as $property) {
$publicProperties[$property->name] = $property->getValue($this);
}
return $publicProperties;
}
Helmut
--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---
Yeah, it seems from these caveats and edge cases and odd behaviours
that this kind of working in PHP still isn't quite ready for prime
time yet. I'm thinking I'll just have to stick with the classical way
of doing it. Shame, cos the whole SPL interfaces idea shows a lot of
promise.
Just an update to get_object_vars so you can specify the protection
level as you can with getProperties would be a good start!
No, if there's a problem with PHP, it's even allowing access to private
members through any means. It's a direct violation of object oriented
concepts.
What's really wrong here is you're trying to handle an object as an
array. An object is not an array - and should not be treated as such.
If you want an array, use an array. If you want an object, use an object.
--
==================
Remove the "x" from my email address
Jerry Stuckle
JDS Computer Training Corp.
jstu...@attglobal.net
==================
Difficult to argue with that.
> What's really wrong here is you're trying to handle an object as an
> array. An object is not an array - and should not be treated as such.
>
> If you want an array, use an array. If you want an object, use an object.
Then why does the SPL provide a set of interfaces to allow objects to
be treated as arrays?
This was only really an experiment anyway, to see how useful the SPL
iterators might be. The answer so far appears to be "not very".
Why did PHP have register_globals and magic_quotes_gpc? And why did it
take them so long to get any real OO concepts in the language?
It's not the only stupid thing they've done.
> This was only really an experiment anyway, to see how useful the SPL
> iterators might be. The answer so far appears to be "not very".
No argument there.
> What's really wrong here is you're trying to handle an object as an
> array. An object is not an array - and should not be treated as such.
IMHO, that's somehow too simplified:
> If you want an array, use an array. If you want an object, use an object.
I want to use collection objects, where I have methods but where I also
can iterate over the elements in the collection and where I can access
elements in the collection in an array-/[]-style. And where I can
restrict the type of elements that are allowed in the collection.
But yes: I agree in not seeing the advantages to map the properties of
an object to a corresponding array.
Helmut
Which is completely different than mapping the properties to array
elements. But unfortunately, PHP doesn't support either restricting the
type in the collection or overloading operators.
It's a shame - although often misused in C++, both have definite
advantages. I also wish we were able to do it.
> But yes: I agree in not seeing the advantages to map the properties of
> an object to a corresponding array.
>
> Helmut
>> I want to use collection objects, where I have methods but where I also
>> can iterate over the elements in the collection and where I can access
>> elements in the collection in an array-/[]-style. And where I can
>> restrict the type of elements that are allowed in the collection.
>>
>
> Which is completely different than mapping the properties to array
> elements.
Yes, it is. I wrote this also in my previous post :). I wrote "too
simplified", because I wanted to show cases, where it might be useful to
have an object, that behaves like an array. Your posting, I replied to,
suggests IMHO, you think the whole ArrayAccess, Iterator, etc. intefaces
are useless.
> But unfortunately, PHP doesn't support either restricting the
> type in the collection or overloading operators.
I'm just trying to implement collection classes that restrict the type
of their elements. It's not 100% perfect, as PHP itself gives you the
retrictions, but:
Because of this thread, I tested the ArrayAccess interface once again
with PHP 5.3 and finally it "works as expected". Which means, if you
implement it and pass an object in the offsetSet() method, it is a
reference now. IIRC, until 5.3, it was a copy, which made this interface
pretty useless.
And the offsetSet() method of the interface gives you also the
possibility to throw an exception, if the provided value is not of the
correct type. But yes: unfortunately you cannot define this in the
methods signature.
I said nothing of the kind. I was specifically addressing Gordon's
topic. If you want to discuss another topic, please start a new thread.
>> But unfortunately, PHP doesn't support either restricting the type in
>> the collection or overloading operators.
>
> I'm just trying to implement collection classes that restrict the type
> of their elements. It's not 100% perfect, as PHP itself gives you the
> retrictions, but:
>
> Because of this thread, I tested the ArrayAccess interface once again
> with PHP 5.3 and finally it "works as expected". Which means, if you
> implement it and pass an object in the offsetSet() method, it is a
> reference now. IIRC, until 5.3, it was a copy, which made this interface
> pretty useless.
>
> And the offsetSet() method of the interface gives you also the
> possibility to throw an exception, if the provided value is not of the
> correct type. But yes: unfortunately you cannot define this in the
> methods signature.
>
> Helmut
>
>
>
> --- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---
Which again is a completely different subject and has no relation to
Gordon's question.