This document represent a proposal for __get __set "improvements", feels 
free to comment it, reject it or why not approve it.

Keywords and function names are just for understanding they should be 
rethinked if the design is accepted.

I took care of Stanislav point of view and tried to merge his conception 
of the accessor system with mine.


What i'm trying to do in this design :

- do not remove overloaded variables declaration from class definition

- make overloaded variables obvious to class reader

- keep the 'undefined' feature into the model. So __get __set 
  __call will be called if the attribute or method is not defined

- limit PHP code length required by accessor usage and make overload   
  easier to maintain (no private hashtable required but still possible
  to use) 

- ensure that __set and __get will be called for overloaded variables 
  even if variable value is set (unless overload is removed for this
  variable)

- prevent recursion

- selective choice of overloaded attributes with no loss of performance

- stay as near as possible from current ZendEngine2 C code.



* the model requires introduction of a new attribute keyword here 
  symbolized by 'overloaded'. Overloading an attribute means binding 
  get and set calls for this attribute to __get and __set class 
  method.

        class Foo {

            /** 
             * Access to $bar is done with __get and __set
             *
             * In this example, __set is not defined, any 
             * call like "$foo->bar = 'value';" will produce
             * a direct access to the variable.
             */
            overloaded $bar;

            /**
             * Regular variable
             */
            var $biz;

            /**
             * __get accessor.
             * 
             * This accessor is called whenever an overloaded
             * object variable has to be retrieved.
             *
             * At this time, it will also be invoqued for 
             * undefined variables.
             */
            function __get($varname) 
            {
                if ($varname == "bar") {
                    // ... code return bar value or anything else
                }
                // ...
            }
        };

        $f = new Foo;
        echo $f->bar;  // calls __get
        echo $f->biz;  // no call to __get

        // same for __set if __set defined
  

* concerning recusion problem two point of view, please comment :

  - disabling recursion in accessors : in __XXX() access to an 
    overloaded variable is direct.

        ++ recursion errors limitation

        ++ no zend_object_handlers.c modification

        -- the coder has to know which attributes requires overloading 
           to call __set and __get when appropriate (limit dynamic 
           generation of overloaded attributes (see below))

        class Foo {
                overloaded $bar;
                var $biz;

                function __set($var, $value)
                {
                        // ...
                        $this->__get($bar); // force call of getter

                        // ...
                        echo $this->bar; // direct access

                        // ...
                        $this->$var = $value; // direct access
                }

                function __get($var)
                {
                        // ...
                        $this->bar;  // no error direct access
                }
        }


    - give functions to bypass accessors

        ++ no need to worry about which attributes requires __get and
           __set as ours getter and setter will be called when 
           appropriate.

        -- the coder has to use the direct access function to break 
           recursion

        class Foo {
            overloaded $bar;
            var $biz;

            function __set($var, $value) 
            {
                // ...
                $this->bar; // call __get

                // ...
                $this->biz; // direct access

                // set value with direct access
                object_set($this, $var, $value);

                // ...
            }

            function __get($var)
            {
                // ...
                $this->bar;  // BEWARE recursive call
                        
                // ...
                object_get($this, "bar"); // direct access
            }
        };


* declaring an overloaded attribute whithout __get or __set has no
  consequence, the attribute is treated as a regular public 
  attribute. (may issue a warning in the future)

        class Foo {
            // ? should ? issue a warning as no __get / __set
            // is defined in the class
            overloaded $foo;
        };


* a way to define dynamically an 'overloaded' variable into the object 
  here i named it 'overload_var' but the name is just for 
  understanding.

        class Foo {
        
            function __set($varname, $value) 
            {
                // if the object doesn't know this attribute
                // we create a new overloaded attribute
                if (!isset($this->$varname)) {
                    // futher modification of this variable will 
                    // invoque the __set method 
                    overload_var($this, $varname);
                }
                $this->$varname = $value;
            }
        };

        $foo->unknownVarName = "value";   // call __set
        $foo->unknownVarName = "other";   // still call __set

        // remove overload on specified attribute, the oposite of
        // overload_var()
        unoverload_var($foo, "unknownVarName");

 Note that the 'overload_var' is optional, when not used, the attribute
 is set like any other public attribute.

 The function overload_var may also dynamically transform a regular
 variable into an overloaded one (please comment).


* a way to prevent calls of accessors on *undefined* variables and
  methods should be provided in the future so the class maintainer
  would decide if its class will handle them or not.

        class Foo {
            function __get($varname)
            {
                // ...
            }
        };

        // do not accept undefined attributes (reactivate error 
        // reporting for this object)
        disable_undefined_overload('Foo');



* Changes from current design :

- new property keyword ('overloaded') 

- a way to keep track of overloaded properties 
  may be a new hashtable in zend_class_entry just like default, private 
  or protected  ones.

(note: zend.h contains a structure named "zend_overloaded_element" it 
seems that no source use it)

- __get / __set called if
        - exists __get / __set          (nothing to modify)
        - the property does not exists  (nothing to modify)
        - the property is declared overloaded  (modification required)

- other changes depends of 
        - recursion choices
        - new functions
        - disabling undefined properties
        - warning issue


* Things to think about :

- inheritance and __set / __get overwrite may requires parent::__set() 
  call leaved to php developper.

- overloaded methods and __call



Let me know what you think about this design. 
Laurent



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

Reply via email to