On Fri, 2002-04-05 at 00:43, Kristian Koehntopp wrote:
> 
> I have several observations regarding the newly implemented
> aggregate() und overload() extensions. Depending on the
> correctness of these observations you may want to reconsider the
> inclusion of these extensions in the current build of PHP.
> 
> The following has not in its entirety verified in the source and
> may be incorrect or incomplete. Please correct me.
> 
> Situation:
> 
> $obj = new Origclass; aggregate($obj, "Classname"); is a
> function which adds all instance variables and methods from
> class Classname to an object $obj of an original class
> Origclass. There are variations of aggregate which allow you to
> add instance variables and methods selectively by enumeration
> and regex.
> 
> Observation:
> 
> aggreate and friends break the introspection in PHP, and may
> interfere with serialization and sessions. This is, because
> get_class($obj) returns Origclass, and no trace of Classname.
> Also, $obj is_a() Origclass, but not is_a(Classname), leading to
> serialization of $obj as a pure Origclass, and reinstantiation
> as a pure Origclass with no methods from Classname at all.
> 
> This is the situation of serialize() in PHP 3 at a slightly
> elevated level. Reminder: In PHP 3, serialize() did not record
> the class of an object on serialization, and unserialize()
> consequently produced an object with no methods at all, which is
> in fact quite useless.
> 
> Also, because of the fine grained nature of the more advanced
> variations of aggregate, there is no fast way to describe the
> class or type of an object instance. The only way to completely
> describe an object is to completely enumerate its instance
> variables and instance methods. Essentially, get_class and
> friends become meaningless.
> 
> Alternative:
> 
> aggregate is a badly designed way to introduce multiple
> inheritance like functionality. There are at least two tested
> ways to implement MI functionality. Statically typed languages
> such as C++ set type == class and implement pure MI. Regarding
> PHP, this would lead to
> 
> class C extends A, B { ... }
> 
> with the order of A and B being important. Also,
> get_parent_class() would need to be modified to return
> inheritance level information or inheritance graph information
> together with class names in order to produce meaningful
> information. Something like
> 
>       $v = get_parent_class($complicated_object); 
> 
> $v would be
> 
>       array("A" => "B", "B" => "C", "B" => "D");
> 
> for
> 
>       class A extends B ...
> 
>       class B extends C, D ...
> 
> An alternative, possibly cleaner way would be the introduction
> of interfaces and categories. In this scenario we keep single
> inheritance. We introduce declarations of interfaces, which may
> contain a number of instance variables and functions. Interfaces
> are a simple declaration of these, there is no implementation at
> all in an interface.
> 
> A class may claim conformity with an interface. This means that
> the class at least has all the instance variables the interface
> demands and has at least implementations for all member
> functions of the interface. The class may implement all this
> independently or it may import a category.
> 
> A category is class-subset, and always an implementation of an
> interface (you cannot define a category for which no interface
> definition is being known).
> 
> Interfaces and Categories are found in some dynamically typed
> languages, sometimes under slightly different names. They
> separate type and class, and in order for them to function in
> PHP we would need enumerative functions and predicates to
> complement the new language constructs (get_defined_interfaces(),
> get_defined_categories(), conforms_to($interface_name)).
> 
> Recommendation:
> 
> As-is aggregate() is a broken design, and will break existing
> older functionality. It will create a number of support issues,
> and later backward compatibility issues. It may inspire even
> more broken "fixes" for these issues, effectively uglifying the
> language itself or at least its object system.
> 
> The best would be to remove aggregate() completely, and bury it.
> It should be replaced by a proper MI system, probably one for a
> dynamically typed language, as PHP is such a language.
> 
> If aggregate() functionality is being used right now, and is
> critical, it should be kept, properly documented, the drawbacks
> spelled out in all its ugliness and its use strongly
> discouraged. It should be clearly marked as obsolete from day 1,
> and may be removed at any time. If someone uses this feature
> despite the warning labels, this person deserves everything that
> happens.

Well, aggregate and MI are for solving different problems, basically
aggregate is for customizing objects at runtime, while MI is done at
compile time.  While I agree that the current implementation will
encounter problems when it gets to serialization and such, I vividly
protest against removing it from the language.  A
redesign/reimplementation is fine.

The current implementation of aggregate is really a short-cut form of
real object aggregation, since it imports methods directly into a
temporary class entry.  This is fast, but agreeably ugly :-).

A better design could be to create object instances of the aggregated
class, have a member array in the aggregating object with a
method=>object map, and proxy calls instead of importing.

To resolve the serialization issue, I don't have any big problems with
removing the finer-grained aggregation methods in favor of a single
predictable one.

Suggestion for new implementation:

* Don't create temporary class entries, proxy all calls through
  PHP's object overloading.

* aggregate() will create an instance of the aggregated class
  and store it in the aggregating object (member __aggregated_objects
  or something like that), and overload the aggregating object.
  The __aggregated_objects array is a hash of "method" => &$object.
  If a method name exists in the hash, issue a warning and keep the
  old version.

* The overloaded function handler looks up undefined methods in
  __aggregated_objects at runtime.

* Properties will not be aggregated

* Constructors, destructors and other special methods (__*) will not
  be aggregated.

It should be possible for deserialize() to recreate objects aggregated
this way.  Do you see any problems with this implementation?

For users, the major distinction here will be that the aggregated
objects will be, well, separate objects, so $this->foo() call foo in the
aggregated object, which again is cleaner and more predictable than when
class entries are merged.  It'll be a little slower because of some
extra hash lookups during method calls, but that's the price.

I'm +0 when it comes to MI.

> Situation:
> 
> The overload() extension allows userland code to intercept get,
> set and call operations on the instance variables and instance
> functions of an object. It does this by defining the special
> fixed name functions __get, __set and __call in a class, which
> are enabled by calling overload() on this class.
> 
> It also allows, according to Sebastian Bergmann, and in spite of
> the current documentation at
> http://www.php.net/ref.overload.php, __get_x(), __set_y() and
> __call_x() functions in order to intercept accesses and calls to
> instance variable x and method y respectively.

(Instance variable y, not method.)

> I haven't verified this in source, but Sebastian claims it
> works.
> 
> Observation:
> 
> If this is NOT the case, I have no issue with overload(). It is
> dangoerous, but it is an expert feature and the design is okay.
> 
> If overload() allows for __get_x(), though, this is bad design.
> The reason for this is that we get namespace pollution and,
> again (*), automatically called callback functions with a
> variable name. There will interoperate badly with inheritance,
> with dynamically created, undeclared instance variables and with
> aggregate().
> 
> Recommendation:
> 
> If overload() indeed supports variably named callback functions
> such as __get_x(), support for this should be removed in order
> to avoid a number of possible inconsistencies and namespace
> pollution.

I don't particularly mind removing __(set|get)_* and only have
__(set|get).  +0 on this one too.

Thanks for your input Kristian.

 - Stig


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

Reply via email to