All objects are handled centrally by the Component Manager which implements the Inversion of Control (IoC) principle and provides some additional features such as a caching mechanism for objects. Because all packages are built on this foundation it is important to understand the general idea of components and the container. This chapter introduces the basic principles behind the Component Manager.
![]() | Tip |
|---|---|
A very good start to understand the idea of Inversion of Control and Dependency Injection is reading Martin Fowler's article on the topic. |
In case you don't want to learn about the Component Manager and the Dependency Injection features yet, please read at least this section. There are a few rules you have to follow, even if you don't use the more advanced features.
Use getComponent() instead of
the new operator!
The instantiation of classes must be handled by the Component Manager. Therefore if you have to instantiate a class or need an existing instance of a class, use the Component Manager's API for retrieving one:
Example 6.2. Ask the Component Manager instead
class F3_MyPackage_SomeClass {
protected $componentManager;
public function __construct(F3_FLOW3_Component_ManagerInterface $componentManager) {
$this->componentManager = $componentManager;
}
public function someFunction() {
$myObject = $this->componentManager->getComponent('F3_MyPackage_MyClassName');
}
}In the above example you ask the Component Manager for the
instance of F3_MyPackage_MyClassName. In
order to get an instance of the Component Manager, just add a
parameter to your class constructor as seen in the example - you
will automatically get a reference
injected.
Singletons and Prototypes
A Singleton
is the name of a Design Pattern which ensures that only one instance
of a class exists at a time. In PHP you can implement the Singleton
pattern by creating a function (usually called
getInstance()), which returns a unique instance
of your class. Although this way of implementing the Singleton will
possibly not conflict with the Component Manager, it is counter
productive to the integrity of the system and might raise problems
with unit testing (sometimes the Singleton is referred to as a
Anti Pattern).
The Component Manager will, if not configured differently, always return a unique instance of a component. Therefore Singleton is the default behaviour! If you need a fresh instance of your class, you will have to configure the scope of it by adding some annotation to your class:
Example 6.3. Sample scope annotation
/**
* This is my great class.
*
* @scope prototype
*/
class F3_MyPackage_SomeClass {
}The idea to segment software into reusable components which can be easily composed into a whole, is as old as software development itself. It is a tempting idea to just combine ready-made components instead of sorting out code from earlier projects which can hopefully be reused. Although the initial vision of mass-produced software with prefabricated components did not become reality, software componentry is undoubtedly a good thing, something you want your application framework to have support for.
Since version 3.5, TYPO3 allows anyone to extend the built-in functionality by developing their own extensions. This plugin concept has proven to be very powerful and led to thousands of publicly available extensions contributed by our community. While a plugin based system is usually based on a monolithic core the plugins can hook onto, a component based system is only composed of components itself. FLOW3, the foundation of TYPO3 version 5, is a component based framework giving you lots of opportunities to cleanly extend and modify virtually any part of the system.
All PHP classes which are managed by the Component Manager are called Components. Because nearly all classes are managed by the Component Manager, almost all classes in FLOW3 - including third-party extensions - are used as components.
![]() | Tip |
|---|---|
All classes which are found in the
|
In contrast to plain, standalone classes which are probably only used in a project or two, the development of reusable components requires the author to pay more attention on proper documentation, testing and encapsulation. If a component is well designed, it doesn't expose the internal functions and properties of the class but only allows access through a well-thought API. By following this blackbox-principle, it is much easier to modify the internal behaviour of a component at a later time, because it is always known which functions can be used from outside.
In simple, self-contained applications, creating objects is as
simple as using the new operator. However, as the
program gets more complex, a developer is confronted with solving
dependencies to other objects, make classes configurable (maybe through
a factory method) and finally assure a certain scope for the object
(such as Singleton or
Prototype). Howard Lewis Shipexplained this circumstances nicely in his
blog (quite some time ago):
Garbage collection is the last stage of an object's life cycle, but there's just as much going on at the start of the object's life cycle. That's why component frameworks and dependency injection containers (such as HiveMind, Spring, Picocontainer and Avalon) are so important.
[...] Once you start thinking in terms of large numbers of objects, and a whole lot of just-in-time object creation and configuration, the question of how to create a new object doesn't change (that's what
newis for) ... but the questions when and who become difficult to tackle. Especially when the when is very dynamic, due to just-in-time instantiation, and the who is unknown, because there are so many places a particular object may be used.
We as PHP developers don't have to care about garbage collection and we surely wouldn't like to be responsible for it either. However, building objects can be even more complex than destructing them. Therefore the FLOW3 framework manages the whole lifecycle of components for you. The Component Manager is a so called Lightweight Container taking care of object building and dependency resolution. We'll discover shortly why dependency injection makes such a difference to your application design.
The Component Manager provides a lean API for registering, configuring and retrieving instances of components. Most of the methods provided are exclusively used within the Core package and should possibly not be used elsewhere. By offering Dependency Injection, the Component Manager helps you to avoid creating rigid interdependencies between components. It allows for writing code which is hardly or even not at all aware of the framework it is working in.
Although Dependency Injection is what you should strive for, it
might happen that you need to retrieve component instances directly.
Instead of using PHP's new operator, you must ask
the Component Manager for the instance of a component.
First, you need an instance of the Component Manager itself and as you have seen in the shortcut example before, one way of getting it is adding a new parameter to your class constructor method:
public function __construct(F3_FLOW3_Component_ManagerInterface $componentManager) {
}To explicitly retrieve the instance of a component use the
getComponent() method:
$myComponentInstance = $componentManager->getComponent('F3_MyPackage_MyClassName');It is possible to pass arguments to the constructor of the
component class just by adding them to the
getComponent() call:
$myComponentInstance = $componentManager->getComponent('F3_MyPackage_MyClassName', 'first argument', 'second argument');By default, the name of a component is identical to the PHP class
which implements the component's functions. A class called
F3_MyPackage_MyImplementation will be
automatically available as a component with the exact same name. Every
part of the system which asks for a component instance with a certain
name will therefore - by default - get an instance of the class of that
name. It is possible to replace the original implementation of a
component by another one. In that case the class name of the new
implementation will naturally differ from the component name which stays
the same at all times.
If the component name is the same as the name of a PHP interface,
it is often referred to as a component type. An
interface called F3_MyPackage_MyInterface
will be available as a component name as long as there exists at least
one class implemententing that interface.
The intention to base an application like the TYPO3 CMS on a combination of packages and components is to force a clean separation of domains which are realized by dedicated components. The less each component knows about the internals of another component, the easier it is to modify or replace one of them, which in turn makes the whole system flexible. In a perfect world, each of the components could be reused in a variety of contexts, maybe even outside the FLOW3 framework.
An important prerequisite for resuable code is already met by encouraging encapsulation through the components approach. However, the components are still aware of their environment as they need to actively collaborate with other components and the framework itself: An authentication component will need a logger for logging intrusion attempts and the code of a shop system hopefully consists of more than just one class. Whenever a component refers to another directly, it adds more complexity and removes flexibility by opening new interdependencies.
By introducing Dependency Injection, these interdependencies are minimized by inverting the control: Instead of asking for the instance of a component actively, the depending component just gets one injected by the Component Manager. This methodology is also referred to as the "Hollywood Principle": “Don't call us, we'll call you.”. It helps in the development of code with loose coupling and high cohesion – or in short: It makes you a better programmer.
In the context of the previous example it means that the
authentication component announces that it needs a logger which
implements a certain PHP interface (eg. the
F3_Log_LoggerInterface). The component
itself has no control over what kind of logger (simple file logger,
sms-logger, ...) it finally gets and it doesn't have to care about it
anyway as long as it matches the expected API. As soon as the
authentication component is instantiated, the component manager will
resolve these dependencies, prepare an instance of a logger and inject
it to the authentication component.
![]() | Tip |
|---|---|
An
article by Jonathan Amsterdam discusses the difference between creating an object
and requesting one (ie. using |
Dependencies on other components can be declared in the components configuration (see section about configuring components). Generally there are two modes of dependency injection supported by FLOW3: Constructor Injection and Setter Injection.
With constructor injection, the dependencies are passed as constructor arguments to the depending component while it is instantiated. Here is an example of the authentication component which depends on a logger component:
Example 6.4. A simple example for Constructor Injection
public class F3_Authentication_LDAPAuthentication {
protected $logger;
public function __construct(F3_Log_LoggerInterface $logger) {
$this->logger = $logger;
}
public function authenticate($credentials) {
$this->logger->log('tried to authenticate');
}
}So far there's nothing special about this class, it just makes
sure that an instance of a class implementing the
F3_Log_LoggerInterface is passed to the
constructor. However, this is already a quite flexible approach
because the type of logger can be determined from outside by just
passing one or the another implementation to the constructor. The next
step is instructing the Component Manager to automatically pass a
component instance to the constructor whenever the authentication
class is instantiated. We do that by defining a little
configuration:
Example 6.5. Components.conf file for Constructor Injection
[F3_Authentication_LDAPAuthentication] constructorArguments.1.reference = F3_Log_LoggerInterface
The two lines above define that a component instance of the type
F3_Log_LoggerInterface must be passed
to the first argument of the constructor when an instance of the
component F3_Authentication_LDAPAuthentication
is created.
If you are lazy enough, you can even skip the last step and
don't configure the component at all! By a mechanism called
Autowiring all dependencies declared in a
constructor will be injected automagically if the constructor argument
provides a type definition (ie.
F3_Log_LoggerInterface in the above
example). Autowiring is activated by default (but can be switched
off), therefore you won't create any constructor reference
configuration most of the time.
With setter injection, the dependencies are passed by calling setter methods of the depending component right after it has been instantiated. Here is an example of the authentication component which depends on a logger component - this time with setter injection:
Example 6.6. A simple example for Setter Injection
public class F3_Authentication_LDAPAuthentication {
protected $someLogger;
public function authenticate($credentials) {
$this->logger->log('tried to authenticate');
}
public function setSomeLogger(F3_Log_LoggerInterface $someLogger) {
$this->someLogger = $someLogger;
}
}Analog to the constructor injection example, a logger component is injected into the authentication component. In this case, however, the injection only takes place after the class has been instantiated and a possible constructor method has been called. The neccessary configuration for the above example looks like this:
Example 6.7. Components.conf file for Setter Injection
[F3_Authentication_LDAPAuthentication] properties.someLogger.reference = F3_Log_LoggerInterface
Unlike constructor injection, setter injection does not offer the autowiring feature. All depedencies have to be declared explicitly in the component configuration.
The dependencies between components are only resolved during the instantiation process. Whenever a new instance of a component class needs to be created, the component configuration is checked for possible dependencies. If there is any, the required components are built and only if all dependencies could be resolved, the component class is finally instantiated and the dependency injection takes place.
During the resolution of dependencies it might happen that circular dependencies occur. If a component A requires a component B to be injected to its constructor and then again component B requires a component A likewise passed as a constructor argument, none of the two classes can be instantiated due to the mutual dependency. Although it is technically possible (albeit quite complex) to solve this type of reference, FLOW3's policy is not to allow circular dependencies at all. As a workaround you can use Setter Injection instead of Constructor Injection for either one or both of the components causing the trouble.
The behaviour of components significantly depends on their
configuration. During the initialization process all classes found in the
various Classes/ directories are registered as
components and an initial configuration is prepared. In a second step,
other configuration sources are queried for additional configuration
options. Definitions found at these sources are added to the base
configuration in the following order:
If it exists, the file
will be parsed and the configuration applied.PackageName/Classes/Components.conf
If it exists, the file
will be included. PHP code found in that file will be executed and
allows for programatically modfying the components
configurationPackageName/Classes/ComponentsConfiguration.php
Additional configuration defined through the component manager user interface will be applied. This feature has not been implemented yet!
Currently there are three important situations in which you want to configure components:
Override one component implementation with another
Set the active implementation for a component type
Define dependencies to other components
As already mentioned, the configuration for each component is
compiled from different sources. The
Components.conf file is probably the most
frequently used of them and is therefore used in most of the examples.
However, the names of the configuration options and their possible
values are identical to all configuration sources.
The Components.conf file basically follows
the .ini file format. Although this format is not standardized, it is
usually well known and widely used. The following example demonstrates
the syntax:
Example 6.8. A sample Components.conf file
; An example for a Components.conf file ; # Comments may either start with a semicolon or # a hash sign. [Component Name]optionName=optionValueoptionName= "option Value with non-alphanumeric chars"
![]() | Note |
|---|---|
Internally the |
As an alternative to the Components.conf
file, it is possible to configure components programatically. If a
file named ComponentsConfiguration.php exists in
the Classes directory of a package, it will be
included during the configuration process (ie. before the first
component class is instantiated!). In general, the PHP file may
contain any code which prepares the environment or configuration for
the components of its package.
The following code adds the same configuration as in the Constructor Injection example which will be used in the next sections:
Example 6.9. Sample ComponentsConfiguration.php file
<?php declare(encoding = 'utf-8'); // Alter the component configuration: $configuration = $parsedComponentConfigurations['F3_Authentication_LDAPAuthentication']; $configuration->setConstructorArgument(new F3_FLOW3_Component_ConfigurationArgument(1, 'F3_Log_LoggerInterface', F3_FLOW3_Component_ConfigurationArgument::ARGUMENT_TYPES_REFERENCE); $configuration->setConstructorArgument(new F3_FLOW3_Component_ConfigurationArgument(2, 'F3_Log_LDAPServerInterface', F3_FLOW3_Component_ConfigurationArgument::ARGUMENT_TYPES_REFERENCE); $configuration->setConstructorArgument(new F3_FLOW3_Component_ConfigurationArgument(3, 'cn=John Smith,ou=TYPO3 Development,o=TYPO3 Association,c=CH'); $parsedComponentConfigurations['F3_Authentication_LDAPAuthentication'] = $configuration; unset($configuration); ?>
![]() | Note |
|---|---|
During the configuration process the
|
![]() | Caution |
|---|---|
Don't register autoloader methods in the
|
A very convenient way to configure certain attributes of components are annotations. You write down the configuration directly where it takes effect: in the class file. However, this way of configuring components is not really flexible, as it is hard coded. That's why currently only the scope option can be set through annotations. It's up to you defining the scope in the class directly or doing it in a Components.conf file – both have the same effect. We recommend using annotations in this case, as the scope usually is a design decision which is very unlikely to be changed.
Example 6.10. Sample scope annotation
/**
* This is my great class.
*
* @scope prototype
*/
class F3_MyPackage_SomeClass {
}One advantage of componentry is the ability to replace components by others without any bad impact on those parts depending on them. A prerequisite for replaceable components is that their classes implement a common interface which defines the public API of the original component. Other components which implement the same interface can then act as a true replacement for the original component without the need to change code anywhere in the system. If this requirement is met, the only neccessary step to replace the original implementation with a substitute is to alter the component configuration and set the classname to the new implementation.
To illustrate this circumstance, consider the following classes:
Example 6.11. A simple Greeter class
class F3_MyPackage_Greeter {
public function sayHelloTo($name) {
echo('Hello ' . $name);
}
}During initialization above class will automatically be registered
as the component F3_MyPackage_Greeter and is
available to other components. In the code of another component you
might find these lines:
Example 6.12. Code using the component F3_MyPackage_Greeter
// Explicitly fetch an instance of the F3_MyPackage_Greeter component:
$greeter = $componentManager->getComponent('F3_MyPackage_Greeter');
// Say hello to Heike:
$greeter->sayHelloTo('Heike');
Great, that looks all fine and dandy but what if we want to use
the much better component
F3_OtherPackage_GreeterWithCompliments? Well, you
just configure the component F3_MyPackage_Greeter
to use a different class:
Example 6.13. Components.conf file for component replacement
[F3_MyPackage_Greeter] # Change the name of the class which represents the component "F3_MyPackage_Greeter": className = F3_OtherPackage_GreeterWithCompliments
Now all components who ask for a traditional greeter will get the
more polite version. However, there comes a sour note with the above
example: We can't be sure that the
GreeterWithCompliments class really provides the
neccessary sayHello() method. The solution is to
let both implementations implement the same interface:
Example 6.14. The Greeter component type
interface F3_MyPackage_GreeterInterface {
public function sayHelloTo($name);
}
class F3_MyPackage_Greeter implements F3_MyPackage_GreeterInterface {
public function sayHelloTo($name) {
echo('Hello ' . $name);
}
}
class F3_OtherPackage_GreeterWithCompliments implements F3_MyPackage_GreeterInterface{
public function sayHelloTo($name) {
echo('Hello ' . $name . '! You look so great!');
}
}Instead of referring to the original implementation directly we can now refer to the interface. In this case we call the component name a component type because it contains the name of a PHP interface.
Example 6.15. Code using the component type F3_MyPackage_GreeterInterface
// Explicitly fetch an instance of any implementation of the F3_MyPackage_GreeterInterface component type:
$greeter = $componentManager->getComponent('F3_MyPackage_GreeterInterface');
// Say hello to Heike:
$greeter->sayHelloTo('Heike');
Finally we have to set which implementation of the
F3_MyPackage_GreeterInterface should be
active:
Example 6.16. Components.conf file for component type definition
[F3_MyPackage_GreeterInterface] className = F3_OtherPackage_GreeterWithCompliments
Any interface found in the Classes/ directory
will equally be registered as a component if at least one class within
the same package was found that implements this interface. The first
class found in the package which implements the interface is considered
to be the default implementation and the component's
className option is set accordingly. Of course it is
still possible that the class name is defined explicitly in the
Components.conf file or any other configuration
source. This is especially important if more than one implementing class
exists.
This section still has to be written ... See ticket #11
As mentioned earlier, the Component Manager allows for injection of straight values or references (ie. dependencies) either by passing them as constructor arguments during instantiation of the component class or by calling a setter method which sets the wished property accordingly. The following sections demonstrate how to pass values and define dependencies to other components.
Regardless of what injection type is used, there are two kinds of value which can be injected:
Straight values are static values of a simple type. They can be strings, integers, booleans, array or even custom objects (ie. objects which are not handled by the Component Manager) and are passed on as they are.
References are names of components (or component types) which represent dependencies to other components. Dependencies are resolved and an instance of the component is passed along.
The following class and the related
Components.conf file demonstrate the syntax for
the definition of Constructor Injection:
Example 6.17. Sample class for Constructor Injection
public class F3_Authentication_LDAPAuthentication {
protected $logger;
protected $LDAPServer;
protected $distinguishedName = '';
public function __construct(F3_Log_LoggerInterface $logger, F3_LDAP_LDAPServerInterface $LDAPServer, $distinguishedName) {
$this->logger = $logger;
$this->LDAPServer = $LDAPServer;
$this->distinguishedName = $distinguishedName;
}
public function authenticate(F3_Authentication_CredentialsInterface $credentials) {
$this->LDAPServer->doSomeMagic($this->distinguishedName);
$this->logger->log('tried to authenticate');
}
}Example 6.18. Sample configuration for Constructor Injection
[F3_Authentication_LDAPAuthentication] ; Inject two component references as the first two arguments: constructorArguments.1.reference = F3_Log_LoggerInterface constructorArguments.2.reference = F3_LDAP_LDAPServerInterface ; Inject a straight value as the third argument: constructorArguments.3 = "cn=John Smith,ou=TYPO3 Development,o=TYPO3 Association,c=CH"
![]() | Note |
|---|---|
It is usually not necessary to configure injection of
references explicitly. It is much more convient to just declare the
type of the constructor arguments (like
|
The following class and the related
Components.conf file demonstrate the syntax for
the definition of Setter Injection:
Example 6.19. Sample class for Setter Injection
public class F3_Authentication_LDAPAuthentication {
protected $logger;
protected $LDAPServer;
protected $distinguishedName = '';
public function authenticate(F3_Authentication_CredentialsInterface $credentials) {
$this->LDAPServer->doSomeMagic($this->distinguishedName);
$this->logger->log('tried to authenticate');
}
public function setLogger(F3_Log_LoggerInterface $logger) {
$this->logger = $logger;
}
public function setLDAPServer(F3_LDAP_LDAPServerInterface $LDAPServer) {
$this->LDAPServer = $LDAPServer;
}
public function setProperty($propertyName, $value) {
if (array_search($propertyName, array('distinguishedName')) !== FALSE) {
$this->$propertyName = $value;
}
}
}Example 6.20. Sample configuration for Setter Injection
[F3_Authentication_LDAPAuthentication] ; Inject two component references: properties.logger.reference = F3_Log_LoggerInterface properties.LDAPServer.reference = F3_LDAP_LDAPServerInterface ; Inject a straight value as the third argument: properties.distinguishedName = "cn=John Smith,ou=TYPO3 Development,o=TYPO3 Association,c=CH"
As you can see, it is important that a setter method with the
same name as the property, preceeded by "set" exists or the setting is
handled by a generic method called
setProperty().
In order to accomplish all the tasks connected with Dependency
Injection and other advanced features, FLOW3 must take full control
over the instantiation of the component classes. Usually it's sufficient
to know that you have to either retrieve an instance manually with the
getComponent method or get them injected by the
Component Manager. From the component's point of view, a few options may
be set to take influence on the instantiation of its class. This section
explains these configuration options and finally outlines the
instantiation process as a whole.
The objects created by the Component Manager all exist in a certain scope. By default, an instance of a component class is unique which makes sure that the exact same object is returned whenever the Component Manager is asked for a specific component. This default scope is called Singleton. Of course other scopes are supported as well:
Table 6.1. Supported scopes
| Scope | Description |
|---|---|
| singleton (default) | The component instance is unique during one request -
each getComponent call returns the same
instance. A request can be an HTTP request or a run initiated
from the command line. |
| prototype | The component instance is not unique - each
getComponent call returns a fresh
instance. |
| session Not yet implemented | The component instance is unique during the whole user
session - each getComponent call returns
the same instance. |
| content Not yet implemented | The component instance is persisted in a Content Repository. |
A great feature of the Component Manager is that components don't have to implement their own mechanism for administrating their scope - it only has to be configured. The following example contains configurations for three components, all living in a different scope:
Example 6.21. Sample Components.conf with different scopes
[F3_MyPackage_ASingletonClass] scope = singleton [F3_MyPackage_APrototypeClass] scope = prototype [F3_MyPackage_ASessionClass] scope = session
![]() | Note |
|---|---|
Unlike in the above example, the recommended way to define the scope of a component is the @scope annotation. |
In most cases a component class will live in the Singleton scope and at most requires a few dependencies passed to its constructor. However, there are times when it becomes neccessary to pass dynamic values as constructor arguments, especially when the component represents an entity and its instances are not unique (Prototype scope). Consider the following classes:
Example 6.22. A simple addressbook
class F3_Address_Adressbook {
protected $addresses = array();
public __construct(F3_iCal_iCalConnectorInterface $iCalConnector) {
...
}
public addAddress(F3_Address_Address $address) {
$this->addresses[] = $address;
}
}
class F3_Address_Address {
public __construct($street, $zip, $town, $country) {
...
}
}This is admittedly not the fanciest implementation of an addressbook, but it should demonstrate two things:
The class F3_Address_Addressbook is
supposed to be a Singleton and obviously depends on a third
component type F3_iCal_iCalConnectorInterface
which is possibly solved by Dependency Injection.
The class F3_Address_Adress represents
the address entity and its instances must not be unique - we surely
want more than one address. The Address component also expects a few
parameters passed to its constructor.
The following code demonstrates how this addressbook can be used and constructor arguments are passed to the Address entity:
Example 6.23. Passing constructor arguments
# Fetch a unique instance of the addressbook:
$myAddressbook = $componentManager->getComponent('F3_Address_Addressbook');
# Create two new addresses and add them to the addressbook:
$newAddress = $componentManager->getComponent('F3_Address_Address', 'Tryggevældevej', '2720', 'København', 'DK');
$myAddresbook->addAddress($newAddress);
$newAddress = $componentManager->getComponent('F3_Address_Address', 'An den Brodbänken', '21335', 'Lüneburg', 'DE');
$myAddresbook->addAddress($newAddress);Injecting dependencies to a constructor function is a common task. Because FLOW3 can detect the type of dependencies a constructor needs, it automatically configures the component to assert that the necessary components are injected. This automation is called Autowiring and is enabled by default for every component. To repeat our favourite example, imagine that your component class needs some kind of logger. All you need to do in order to get one is writing the following constructor:
Example 6.24. An autowired logger
public function __construct(F3_Log_LoggerInterface $logger) {
$logger->log("Hooray");
}The constructor of the above example will get an instance of the
component of type F3_Log_LoggerInterface
injected and can use it for further operations.
If, for some reason, you need to disable autowiring support, you can do so by setting an option in your component configuration:
Example 6.25. Turning off autowiring support in Components.conf
[F3_MyPackage_MyComponent] autoWiringMode = off
The lifecycle of a component object goes through different stages. It boils down to the following order:
Solve dependencies for constructor injection
Create an instance of the component class
Solve and inject dependencies for setter injection
Live a happy component-life and solve exciting tasks
Dispose the component instance
Your component might want to take some action after certain of the above steps. Whenever one of the following methods exists in the component class, it will be invoked after the related lifecycle step:
No action after this step
During instantiation the function
__construct() is called (by PHP itself),
dependencies are passed to the constructor arguments
After all dependencies have been injected (through constructor- or setter injection) the component's initialization method is called. The name of this method is configurable (see below) and it is called regardless of whether any dependencies have been injected or not
During the life of a component no special lifecycle methods are called
On disposal, the function __destruct() is
called (by PHP itself)
As you can see from the above list, there is only one special method which is provided by PHP's own means and that is the initialization method. Here's a simple example:
Example 6.26. A component class with an initialization method
class F3_MyPackage_MyClass {
protected $logger;
public function __construct(F3_Log_LoggerInterface $logger) {
$this->logger = $logger;
}
public function intializeComponent() {
$this->logger->log('MyClass has been initialized.');
}
}The above example will just work out of the box without any further configuration. However, if you don't have control over the name of your initialization method (maybe, because you are integrating legacy code), you can configure the name of the method in the component configuration:
Example 6.27. Components.conf configuration of the initialization method
[F3_MyPackage_MyClass] lifecycleInitializationMethod = myInitialize