|
I. Introduction to UML link aggregation An object aggregates another when it contains one or more instances, and that these bodies did not particularly help in a specific task (both its responsibility). 2 Although these objects are then paired, it may very well live as one without the other. Example: A lift aggregates of people: it can aggregate several people, it can exist without people (it is empty), and people can exist without being in the elevator. On the contrary, the composition is an aggregation particular: it is not shared and not isolated. Example: A hotel consists of room. If the hotel is destroyed, all rooms are. One room is not part of only one hotel at the time, it is not shared, and is not isolated: a room is a mandatory part of a hotel and can not exist without. In UML, an aggregation materializes by a small white diamond, a composition by a black diamond. In PHP, there is no way to differentiate, in the source code, an aggregation of a composition.
Aggregation Composition II. Agregator: our class mother II-A. The UseCase (when used) Usually, when you want to aggregate object B in an object A, you proceed as follows: Create a Class A property, not public in general, which will store bodies B Create a setter setB () welcoming objets B, and a getter getB (), recovering an object B You've done an aggregation :-) Note that the links "association" and "aggregation", are difficult to define, great thinkers and design UML also vaguely defined aggregation. However, the principle outlined here is found in many biblical references, we will accept it. We will try to autommatiser this process. If our object A must aggregate B, but also C, D, E ... It will become difficult to write all methods and properties. In reality, such cases do not meet, or very rarely. The game is therefore not only the candle, this article would therefore have no real application, if not show you the great flexibility of the object model of PHP. Thus our class A will inherit Agregator, in which there will be any logic control aggregations. The specifications are as follows: Any object of inheriting Agregator should be able to use a method setSomething (). Something must represent a non-public ownership of the class. It can not be assigned to the property something that an object instance of the class Something (it will obviously be determined) Use case <? PHP class aggregator extends Agregator ( Protected $ foo; Protected $ bar; public $ anaska; ) class Foo () Bar class () $ aggregator = new aggregator (); $ aggregator-> setFoo (new Foo); / / OK $ aggregator-> setBar (new Bar); / / OK $ aggregator-> setAnaska (new Bar); / / KO: $ anaska is public $ aggregator-> setAnything (new AnyThing) / / KO: property (and class) Anything is no $ aggregator-> setBar ( 'hello world') / / KO 'hello world' is not an instance of Bar. We then add a feature, which is actually quite common: to make the aggregator itérable so that we "make" all what it contains: iteration of the aggregator <? PHP foreach ($ aggregator as $ objetagrege) ( var_dump ($ objetagrege); ) / * Must display something like this: object (Foo) # 2 (0) () object (Bar) # 4 (0) () object (Bar) # 5 (0) () all our aggregate objects whatsoever * /  agregator
II-B. Design of the superclass To achieve such a result, you doubt that the class mother, Agregator, contains a piece of code. This code is interesting for those who want to learn more about reflection, and the SPL Agregator.php <? PHP abstract class Agregator ( private $ props = array (); final private function __call ($ fun, $ args) / / method "parameter set ()" ( if (substr ($ fun, 0, 3) == "set" & & / / if the method starts with "set" array_key_exists (0, $ args) / / and it has at least one argument ) ( $ class = new ReflectionClass ($ this); try (/ / parameter is there in this class? $ param = new ReflectionProperty ($ this, $ paramName = strtolower (substr ($ fun, 3))); ) catch (ReflectionException $ e) (/ / No? keep the behavior of PHP: no exceptions trigger_error ( "Call to undefined method". get_class ($ this )."::$ fun () ", E_USER_ERROR) / / error: the method" parameter set () "does not exist ) if ($ param-> isPublic () & & / / parameter is it not? $ args [0] instanceof $ paramName) (/ / argument is this an instance? if (! is_array ($ this-> $ paramName)) ( $ this-> $ paramName = array () / / whatever type of setting, it is a forced table ) array_push ($ this-> $ paramName, $ args [0]) / / which we add the argument "object" if (! in_array ($ paramName, $ this-> props)) ( $ this-> props [] = $ paramName / / we store for later iterate ) ) ) else ( trigger_error ( "Call to undefined method". get_class ($ this )."::$ fun () ", E_USER_ERROR); ) return $ this; ) ) We define a method __call () which will intercept methods setSomething (). We declare the final so that it can not be redefined. In fact, the child can change behavior if it redefines __call (), forgetting to call the parent, for example. Then the API thinking comes in. This API introduced at the same time that the object model PHP5, can introspecter in classes and objects to be excavated. Here, we analyze $ this to be resolved in the object that will inherit Agregator, then we check it has a non-public ownership of the same name as that of the method, deprived of the party 'set', and past lowercase. In all cases, we do not prefer lifting of emergency to ensure the default PHP (through errors). Note that we manage collections of forums. We can draw several times the same 'setter', it will store all instances of objects aggregated into a table. Our method __call () return $ this, so we can create an interface fluid and thus write $ aggregator-> setA (new A) -> setB (new B) -> setC (new C )..... II-C. SPL in action: iterators Very often when aggregation ago iteration. So why not add an iterator our class mother agregator? It's very simple and more: just iterate on all non-public properties of the object, and if they have been recently affected, we must return the bodies of objects aggregate. Fortunately, SPL provides RecursiveArrayIterator, and the interface Iteratoragregate we will be quite useful: Following agregator.php <? PHP abstract class Agregator implements Iteratoragregate ( / / ... Following the class back ... final public function getIterator () ( $ array = array (); $ self = new ReflectionObject ($ this); foreach ($ self-> getProperties () AS $ property) ( $ array [] = $ property-> getName (); ) $ props = array_intersect ($ this-> props, $ array); $ array = array (); foreach ($ props as $ prop) ( $ array [] = $ this-> $ prop; ) return new RecursiveIteratorIterator (new RecursiveArrayIterator ($ array)); ) As a result, it is possible to iterate our main class, and it will give us all directly objects being aggregated: Using the iterator <? PHP class aggregator extends Agregator ( Protected $ foo; Protected $ bar; Protected $ foobar; ) class Foo () Bar class () class Foobar () $ ag = new aggregator (); $ ag-> setFoo (new Foo); $ ag-> setBar (new Bar); $ ag-> setBar (new Bar); $ ag-> setFoobar (new Foobar); foreach ($ ag as $ object) ( Var_dump ($ object); ) / / Display object (Foo) # 1 (0) () object (Bar) # 2 (0) () object (Bar) # 3 (0)) (object (Foobar) # 4 (0) () A bug in RecursiveArrayIterator prevents iterator returning objects. This part of the code (iteration objects aggregate) does that from PHP5.3 III. Conclusion To the question "what this example will it serve concrêtement me?" I say, "nothing". In PHP, multiple inheritance does not exist, and if you inherit Agregator, you will not be able to inherit anything else. In the same way: you will not be able to define method __call () in your children as it is declared final. It's done on purpose to force the child to implement this process. Anyway, the aim was to show that the API is very practical consideration: it allows introspecter in classes, objects, functions, parameters to analyze and change behavior on the fly .
|