Table of Contents
The SignalSlot component implements a mechanism for inter- and intra-object communication through the use of signals and slots. Signals are emitted through instances of the ezcSignalCollection class and connected to as many functions as needed. A function that is connected to a signal is called a slot. When the signal is emitted, all connected slots will be run in the order they are connected or in the order of the priorities specified when connecting to the signals.
The SignalSlot library supports signals with arguments that are passed on to the slot. Slots must accept all the parameters that are sent to it. The SignalSlot component also supports prioritized slots. Using this mechanism, you can ensure that a slot is run either before or after other slots.
There are many different variations of the Signal/Slot model. The original implementations required you to derive from the Signal class in order to have signals or slots. This is acceptable for languages that support multiple inheritance. Newer implemtentations circumvent this problem by representing signals through one instance of the Signal class. In other words, a class that has two signals will have two member variables holding instances of the Signals class. This approach can lead to the creation of a lot of signal objects, which can be especially problematic for scripting languages. As a result, our signal class can represent several signals.
This section gives you an overview of the main classes in the SignalSlot component.
This first example shows the simplest possible usage of the signal system. We simply create a new ezcSignalCollection class, connect the "sayHello" signal to the function "hello" and emit the signal.
The output of this example yields the classic "Hello world".
Slots are not limited to normal functions. They are specified using the pseudo type callback. You can specify normal functions, class methods and static class methods, as shown by these variations of the above example.
The typical usage of signals is to have classes emit them when certain events occur. The following example shows a data object that emits a signal when its data is changed. The signal is connected to the caching system, which clears the cache when the data is changed.
This example also shows how you can delay the construction of the ezcSignalCollection object. This can be useful in applications where signals are rarely used and you want to avoid the overhead of creating the object.
Signals can have parameters that will be passed on to the receiving slots. The next example is an extension of the previous example, where the data object gives some information about the data that was changed. This information is passed to the receiving slots as a parameter.
Note that slots must accept the parameters that are passed to them. You will get errors / warnings if you provide too few parameters to the slots you have connected to a signal.
Also note that it is not possible to pass signal parameters by reference except for object types, which are always references in PHP 5.
It is possible to connect several slots to one signal. This is just a matter of calling "connect" several times. You can freely mix the different slot types. The following example adds another connection to our previous example. It regenerates the cache after it has been deleted.
The previous example showed how to connect several slots to one signal and how they are executed one after another. The example code is also dependent on the order the slots are executed. Normally, slots are executed in the order they are connected. Sometimes, it is not possible to connect slots in the order you want them to be executed. To ensure that some slots are executed before others, you can use the priority system. When connecting, you can specify a priority for that connection. Priority numbers can be specified using any positive integer. The lower the number, the higher the priority. Higher priority connections are executed first. By default, connections are made with a priority of 1000.
This example shows how the connections from the previous example could have been specified to ensure the order in which the slots are called.
Excessive usage of the priority system can lead to unmaintainable code, since it is hard to track the various priorities that are in use in a system. We do not recommend using it unless absolutely neccessary.
Sometimes it is useful to connect a signal with a specific name, regardless of the sender ezcSignalCollection, to a slot. Consider what would happen if the Data class from our previous example was extended to a DataObject class with a potential of thousands of instances. If you wanted the caching system to work, you would have to connect each one to the caching system upon creation.
This is both unpractical and time consuming. The solution is to use static connections. When creating ezcSignalCollection objects, you can provide an identifier to the constructor. Using ezcSignalStaticConnections, you can connect to all signals from a source with a specific identifier.
The following example shows how to use static connections to connect the signal "dataChanged" from all objects of the type "DataObject" to the caching system.
You can freely mix static connectons and normal connections. Static connections with the same priority as normal connections are executed after the normal connections.
Lazy initialization is a mechanism to load and configure a component, only when it is really used in your application. This mechanism saves time for parsing the classes and configuration, when the component is not used at all during one request. You can find a description how you can use it for your own components and how it works in the ezcBase tutorial. The keyword for the signal slot component is ezcInitSignalStaticConnections.
This example shows a very simple signal setup with only one signal mapped to a custom function, which just dumps the passed data. The main difference, compared to earlier examples, is that we roll out the configuration to an own class, and define a callback using ezcBaseInit::setCallback to this class, which will be called with the static connections manager as a parameter on the first request for a yet uninitialized signal connection.
ezcBaseInit::setCallback accepts as a first parameter a component specific key, which lets the component later request the right configuration callback. This class must implement the ezcBaseConfigurationInitializer class. The second parameter is the name of the class to perform the static callback on. Each component's lazy initialization calls the static method configureObject() on the referenced class.
When a signal connection is really created and used, like shown in line 22 of the example, the collection will be configured by the custom configuration class. The used identifier "default" may be used for the default signal collection. It of course works for other signal collections, too, like the example for static connections shows.