Having thought a bit about this, there are a couple of initial
problems I see, and, more importantly, I'm not convinced that the
stated problem (encapsulation) requires the addition of a new language
construct (i.e. a "property" as distinct from a "class member"). In
fact, I think it is better implemented in another way (see below).

First of all from a confusion point of view, PHP already has defined
"property" in a way that opposes your RFC's definition:
property_exists() checks for the existence of what you call a "class
member". So, at least, it seems to me you would have to find new
terminology if you wanted to pursue this avenue of a new construct.

In my opinion, however, there should not be a new construct, and
"property" and "class member" should remain interchangeable
descriptions of the same class construct. I think we can do this and
still fix the encapsulation problem.

(Note: this is a long e-mail, and probably best represented in a new
RFC. I have also written this up more completely in the form of an
RFC, but I don't want to clutter up the wiki's RFC page unless other
people like this idea as well, so I'll add the RFC if it looks like it
will be useful).

For example, we could do:

class Time {
    protected $time;

    // note that $seconds, $minutes, and $hours are, in this
    // implementation, "dummies", since they will never hold
    // a value, because their "set" functions only set the $time
    // class member.
    public $seconds;
    public $minutes;
    public $hours;

    public function issetTime($name) isset($hours,$minutes,$seconds) {
        return isset($this->time);
    }

    public function unsetTime($name) unset($hours,$minutes,$seconds) {
        unset($this->time);
    }

    public function setTime($name, $value) set($hours,$minutes,$seconds) {
        switch($name) {
            case 'hours':
                $this->time = $value * 3600;
            break;
            case 'minutes':
                $this->time = $value * 60;
            break;
            case 'seconds':
                $this->time = $value;
            break;
        }
        $this->seconds = $seconds;
    }

    public function getTime($name) get($hours,$minutes,$seconds) {
        switch($name) {
            case 'hours':
                return $this->time / 3600;
            break;
            case 'minutes':
                return $this->time / 60;
            break;
            case 'seconds':
                return $this->time;
            break;
        }
    }
}

with this syntax, you could group the properties like above or, if you
preferred, you could have a different function for each property.

for read only (similar for write only), including the possibility of asymmetry:

class Time {
    protected $time;

    public $seconds;
    public $minutes;
    public $hours;

    // ...

    // now Time::$hours can only be set from inside the class / descendents
    protected function setHours($name, $value) set($hours) {
        $this->time = $value * 3600;
    }

    // but Time::$hours can still be retrieved from outside the class
    public function getHours($name) get($hours) {
        return $this->time / 3600;
    }
}

for interfaces:

interface TimeClass {
    public function setHours($name, $value) set($hours);
}

This has a number of benefits:

1. This "acts like PHP", and also brings along features "out of the box"
(a) The new syntax mimics the form of the syntax of closures ($x =
function() use($a, $b) { // ... };), so it is somewhat familiar.
(b) The arguments of the isset, unset, getters and setters is
analogous to that in the __get and __set methods, so userland
implementation will be familiar.
(c) The getters and setters are still regular class methods, so they
can be called as methods as usual (subject to visibility, of course)
(d) Adds "shades" to the read-only and write-only concepts via
visibility modifiers!
(e) Isset / unset make sense now! E.g. that when unsetting $hours, we
actually want to unset the time in general. In fact, this is
preferred, because in the examples we've been looking at, the "magic"
is in "grouping" the three class members (seconds, minutes, hours),
and this implementation of isset and unset allow you to do just that
grouping, without having to write 3 isset and 3 unset functions, as
you would have to in the property definition.
(f) This allows for much more compact class definitions, as you don't
have to define getHours(), getMinutes(), getSeconds() individually if
you don't want to (although it allows you the flexibility to do that
if it's what you want).
(g) Inheritance works out of the box.
(h) Final keyword works out of the box.
(i) Documentation works out of the box (just do it as you do it now,
on the class members and the methods).
(j) Interfaces work.
(k) In the example above, the class could go ahead and set the
Time::$time class member directly without incurring the overhead of
the getter / setter functions!

2. Fixes the stated problems
(a) Adds the syntactic sugar to complex getting and setting.
(b) Gives read-only / write-only
(c) Fixes the ambiguity of __get and __set in terms of what class
members are defined.

-----------

On another note, given our current parser, as I understand it,
defining the new keywords of "get" and "set" will not be possible
because of a BC break - for people using get and set as the names of
classes, methods, or functions. This means that for both your RFC and
for my suggestion, a new keyword will have to be found, unless this is
put in a new major version and people are comfortable with it. I also
think __get, __set are out because they are in use as the magic
methods...any suggestions?

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to