Software

Ding: Dependency Injection in your PHP Applications

  1. About this manual
  2. The beans, the container, Inversion Of Control, and Dependency Injection
  3. Events and event driven programming support
  4. Using Resources
  5. Internationalization (i18n) support
  6. Aspect Oriented Programming
  7. Configuring the container
  8. The MVC Implementation
  9. The Syslog helper
  10. The PAMI helper
  11. The PAGI helper
  12. The TCP Client helper
  13. The TCP Server helper

1. About this manual


This manual, its samples, and Ding itself are not inteded to be an introduction or tutorial about dependency injection, inversion of control, aspect oriented programming, php, or anything that is not Ding itself. If you are not familiar with these concepts, you may still use Ding (since its really easy to do so).

However, you are encouraged to read about these subjects, just search the web and you will find a lot of good articles, books, and tutorials. Also, having some knowledge or experience using the Spring framework, Seasar, or Sean is not required, but useful when using Ding.

Back

2. The beans, the container, Inversion Of Control, and Dependency Injection


Beans

A Bean is just an object. We call it bean because this particular object obeys some rules and this rules and the bean's lifecycle are managed entirely by what is called a container.

In the java world, a bean is a POJO (Plan Old Java Object), meaning that virtually any concrete class definition is a bean. Since the latter also applies in the php world, we should say that a bean is a "POPO" (Plain Old PHP Object).

Scopes

The scope of the bean defines its lifecycle (when it is created and when should be destroyed). The container provides the following options as scopes:

  • Singleton:The container will have a single instance for the bean (lazy created), and will return this instance every time this bean is asked for.
  • Prototype:The container will return a new instance every time this bean is asked for.

Bean metadata

For each managed bean by the container, exists a bean definition, which composes all the metadata needed by the container and its extensions to actually create and modify the bean's behavior.

This data is generated, and modified by "Providers". Providers are registered in the container. Currently, Ding supports 3 different providers: "xml", "yaml", and "annotations".

The first two read and parse a xml/yaml file (normally called beans.xml/beans.yaml) and the latter generates metadata from the annotations found in each of the classes of the project and their methods.

Any number of providers can be used at any given time, they are supposed to complement each one's metadata. So for example you may use an annotation in your class at the same time that is declared (with the same or different features or properties) in the beans.xml/beans.yaml file.

This metadata includes:

  • Properties:Optional. Properties have also its own metadata (see below).
  • Constructor arguments:Optional. They almost identical to properties, and have their own metadata (see below).
  • Aspects:Optional. All aspects applied to this bean. Aspects have their own metadata (see below).
  • Name:Mandatory. The bean's name.
  • Class:Mandatory. The bean's full class name (including namespace, like A\B\Foo.
  • Scope:Mandatory. The bean's scope. This informs the container how to handle the lifecycle of this bean. The scope is singleton by default.
  • Annotations:Optional. All class-level annotations. Annotations have their own metadata. (see below).
  • Init method:Optional. You may declare an optional method to call right after the bean is constructed and its properties injected so you can initialize internal stuff.
  • Shutdown methodOptional. You may declare an optional method to call when the container is destroyed, so you can cleanup internal stuff.
  • Method annotations:Optional. All method-level annotations.
  • Method injections:Optional. All methods that must return a prototype scoped bean (dependency).
  • Bean dependencies:Optional. Which beans need to be created before creating this bean. This has nothing to do with properties that reference other beans (those are resolved when creating each property), but with indirect dependencies (for instance, bean A depends on bean B which in turns depends on bean C, so in order to create bean A, we need to create C and B).
  • Factory Method:Optional. If specified, will call a static method of Factory Class, a method of Factory Method, or a static method of the bean class in order to create the bean (instead of using the constructor).
  • Factory Class:Optional. If specified, informs the container to call a static method (see Factory Method) of the given class to create the bean instead of using the constructor.
  • Factory Bean:Optional. Same as factory class, but will use a method for the given bean.
  • Abstract:Since 1.1.0. False by default. Specifies a bean as concrete or abstract. Abstract beans cant be instantiated.
  • Parent:Since 1.1.0. Optional. The name of parent bean, if any.
  • Aliases:Since 1.1.2. Optional. An array of strings, with the aliases known for this bean.

Examples:

Generic bean definition (annotations provider)

If you're interested in using annotations, take a look at the following articles:

This is pretty much the same as Java Configuration. You can define a class and annotate it with @Configuration. All methods of this class (or classes) annotated themselves with @Bean will became a source for bean definitions.

@Configuration classes become factory beans. So, a class annotated with @Configuration, having a method called "someBean" which is in turn annotated with @Bean, will become the factory bean and factory method for the bean named "someBean". See docs/examples/ConfigurationAnnotation. (JSR-330)

@Bean can also be used inside methods of @Component's.

This behavior can be overriden if you use the argument name= in the @Bean annotation, like:

@Bean(name=someOtherName)

The string "someOtherName" is now the final name of the bean (although the method is still used as the factory method). (JSR-330)

For every of the @Bean annotated methods, you should also specify:

@Scope(value=xxx) Where xxx is one of prototype or singleton.

@Singleton same as @Scope(value="singleton"). This is the default scope for beans.

@Prototype same as @Scope(value="prototype")

@InitMethod(method=xxx) Where xxx is the method to call to initialize the bean.

@PostConstruct: Using this annotation in a method is equivalent to @InitMethod(method="xxx")

@DestroyMethod(method=xxx) Where xxx is the method to call to destroy the bean on container shutdown.

@PreDestroy: Using this annotation in a method is equivalent to @DestroyMethod(method="xxx")

@Component: Ding also supports @Component annotation. If you annotate a class with this annotation, it will become a bean without needing to specify a @Configuration class. Example:

@Named: At the class level, this annotation is an alias for @Component.

/**
 * This is our bean.
 * @Component(name=myBean)
 * @InitMethod(method=init)
 * @Scope(value=singleton)
 */
class MyBean
{
    /**
     * @Resource
     */
    private $myDependency;

    public function init()
    {
        echo "init\n";
    }
    /**
     * @PreDestroy
     */
    public function destroy()
    {
        echo "destroy\n";
    }
    public function __construct()
    {

    }
}

/**
 * @Component(name=myDependency)
 * @Singleton
 */
class MyDependency
{

}

The full example is available here.

Since version 1.1.2, Ding supports bean aliasing, so you can give multiple names for a @Component, like so:

/**
 * @Component(name={name1, name2, name3})
 */
class MyBean
{
...
}

The full example is available here.

Since 1.3.8: You can also declare beans inside methods of components:

/**
 * @Component(name={name1, name2, name3})
 */
class MyBean
{
    /**
     * @Bean
     */
    public function myBean()
    {
        return new ABean();
    }
}

@Required Use this one in your setter methods (i.e: setSomething()) and this will tell the container to throw an exception if this property isnt configured when instantiating the bean. (JSR-330)

@Resource: Use it in setters AND/OR properties to resolve the injected dependency by name. So for example, the method setDependency() will resolve to a bean named "dependency". (JSR-250).

Since 1.5.0: @Resource can also accept an optional name to indicate the name of the bean to be injected:

/**
 * @Component
 */
class MyBean
{
    /**
     * @Resource(name="aBeanName")
     */
    protected $property;

    /**
     * @Resource(name="aBeanName")
     */
    public function anotherDependency($value)
    {
    }
}
/**
 * @Configuration
 */
class SomeBeanProviderClass
{
    /**
     * @Resource
     */
    protected $dependency;

    /**
     * @Bean
     * @Scope(value='singleton')
     * @InitMethod(method=aMethod)
     * @DestroyMethod(method=bMethod)
     */
    public function someBean()
    {
        $ret = new MyBean();
        $ret->setSomeProperty('hello world');
        return $ret;
    }
    /**
     * @Bean(name=anOverridenName)
     * @Scope(value='singleton')
     * @InitMethod(method=aMethod)
     * @DestroyMethod(method=bMethod)
     */
    public function aNormalMethod()
    {
        $ret = new MyBean();
        $ret->setSomeProperty('hello world');
        return $ret;
    }

    /**
     * @Bean
     * @Value(value=${aProperty})
     * @Value(value=${anotherProperty})
     */
    public function aBeanWithConstructorArguments($arg1, $arg2)
    {
        return new Something($arg1, $arg2);
    }
}

Notice how to inject constructor arguments for beans that come from a @Configuration class. The bean named "aBeanWithConstructorArguments" has been annotated with 2 constructor arguments (the @Value annotations), specified in order of appearance.

DI between configuration classes can also be achieved. I.e: annotate a property of a configuration class with @Resource. This will tell the container to look for a bean that is named like the annotated property. This bean can be provided in another @Configuration annotated class. See docs/examples/PropertiesAnnotations.

Since version 1.1.2, you can use bean aliasing with @Bean. Example:

/**
 * @Configuration
 */
class SomeBeanProviderClass
{
    /**
     * @Bean(name={name1, name2, name3})
     */
    public function someBean()
    {
    ...
    }
}

The full example is available here.

Generic bean definition (xml and yaml providers)

The examples here are based on the xml provider. If you want to take a look at the yaml syntax (fully compatible and almost equal to the xml syntax), please take a look at the yaml example

For a prototype bean (multiple instances allowed):

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype"/>
</beans>

For a singleton bean (only one instance allowed):

<beans>
    <bean id="beanName" class="A\B\C" scope="singleton"/>
</beans>

All options in the above examples are *MANDATORY*. What follows are examples with optional arguments:

This one will first create aBean, bBean, cBean before creating "beanName":

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype" depends-on="aBean, bBean, cBean"/>
</beans>

This one will call A\B\C::getInstance() (static method) in order to get a bean:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype" factory-method="getInstance"/>
</beans>

This one will first create "anotherBean" and then call anotherBean->getInstance() (instance method) in order to get the other bean:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype" factory-bean="anotherBean" factory-method="getInstance"/>
</beans>

This one will call init() as soon as the bean is created and all its properties are injected but before returning the instance to the caller:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype" init-method="init"/>
</beans>

This one will call destroy() when the container is destroyed:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype" destroy-method="destroy"/>
</beans>

Since version 1.1.2, Ding supports bean aliasing, so you can give multiple names for a bean. The id property specifies *the* name of the bean, while the name property specifies the aliases. Example:

<beans>
    <bean id="beanName" class="A\B\C" name="alias1, alias2, alias3"/>
</beans>

  • XML provider example
  • YAML provider example

Constructor arguments types and metadata

The constructor arguments for a bean behave exactly like properties. See the sections below.

Properties types

  • Array:An array has a key (mandatory) and 1 value per key (which in turn can be of any type, so you can have arrays inside arrays, etc).
  • Scalar value:A final value, like a string, an integer, etc.
  • Bean reference:A string containing the name of another bean defined in the container.
  • Code:PHP code to be eval()'uated by the container.

Properties metadata

  • Name:The name of the property. The name is important for setter injection, i.e: a property called 'name' will be injected calling setName()
  • Type:See above for properties types
  • Value:The actual value, whatever the type. The value can also be a string like ${some.value}, which resembles java properties. The container will notice this and will put the correct value (assuming you passed the right value when instantiating the container). Special values like false, true, and null are specified explicitely (see below).

The PropertyDriver, is responsible for getting the bean "PropertiesHolder", where any number of php ini files can be specified from where to read more properties. It will then register these properties in the container. Please read this for the details.

Examples:

Define a scalar property:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <value>aValue</value>
        </property>
    </bean>
</beans>

Define a scalar property with special values like false, true, and null:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty1">
            <null/>
        </property>
        <property name="someProperty2">
            <true/>
        </property>
        <property name="someProperty3">
            <false/>
        </property>
    </bean>
</beans>

Define a scalar constructor argument:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <constructor-arg>
            <value>aValue</value>
        </constructor-arg>
    </bean>
</beans>

Mixing both:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <constructor-arg>
            <value>aValue</value>
        </constructor-arg>
        <property name="someProperty">
            <value>aValue</value>
        </property>
    </bean>
</beans>

The following examples also apply to constructor arguments

Define a scalar property with a container property:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <value>${some.value}</value>
        </property>
    </bean>
</beans>

Define a reference to another bean as a property:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <ref bean="otherBean"/>
        </property>
    </bean>
</beans>

Define a property as code to be evaluated:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <eval><![CDATA[ return "Some evaluated code"; ]]></eval>
        </property>
    </bean>
</beans>

Define an array as a property:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <array>
                <entry key="0"><ref bean="otherBean"/></entry>
                <entry key="1"><value>aValue</value></entry>
                <entry key="2"><eval><![CDATA[ return "Some evaluated code"; ]]></eval></entry>
                <entry key="3"><array><entry key="other"><value>${aValue}</value></entry></array></entry>
            </array>
        </property>
    </bean>
</beans>

Array keys are optional, so you can also do this:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <array>
                <entry<ref bean="otherBean"/></entry>
                <entry<value>aValue</value></entry>
                <entry<eval><![CDATA[ return "Some evaluated code"; ]]></eval></entry>
                <entry<array><entry key="other"><value>${aValue}</value></entry></array></entry>
            </array>
        </property>
    </bean>
</beans>

You can also use properties to declare bean classes, so you can choose/switch implementation at real time:

<beans>
    <bean id="beanName" class="${aCustomImplementation}"/>
</beans>

When using annotations, you can also inject the container properties into the bean properties:

/**
 * @Component(name="myBeanName")
 */
class MyBean
{
    /**
     * @Value(value="${user.property}/asd")
     */
    protected $someProperty;
}

Suppose we defined "user.property" to be "myValue", then in this example, the property "someProperty" will be injected with "myValue/asd".

Using names in constructor arguments

Since 1.3.6 its possible to specify a name for a constructor argument, like:

/**
 * Example with @Component beans.
 * @Component
 */
class MyComponent
{
    /**
     * @Value(name="arg1", value=${arg1}"
     * @Value(name="arg2", value=${arg2}"
     * @Value(name="arg3", value=${arg3}"
     */
    public function __construct($arg1, $arg2, $arg3)
    {
    }
}

/**
 * Example with @Bean methods.
 * @Configuration
 */
class MyConfiguration
{
    /**
     * @Value(name="arg1", value=${arg1}"
     * @Value(name="arg2", value=${arg2}"
     * @Value(name="arg3", value=${arg3}"
     */
    public function myBean($arg1, $arg2, $arg3)
    {
        return new MyComponent($arg1, $arg2, $arg3)
    }
}

In Xml:

<bean id="myBean" class="MyComponent">
  <constructor-arg name="arg1"><value>${arg1}</value></constructor-arg>
  <constructor-arg name="arg2"><value>${arg2}</value></constructor-arg>
  <constructor-arg name="arg3"><value>${arg3}</value></constructor-arg>
</bean>

In Yaml it's more natural, just give the key names for constructor argument a name.

Inner beans

You can define beans inside properties and constructor arguments, like this:

<beans>
    <bean id="beanName" class="D\E\F" scope="prototype">
        <property name="someProperty">
            <bean class="A\B\C" scope="prototype"/>
        </property>
    </bean>
</beans>

Ding will automatically choose a name for this inner bean. The same syntax applies to constructor arguments as well.

Method injection

Suppose you have a singleton bean, and you need to have one of its methods to return an instance of one of its dependencies, but this dependency has the scope 'prototype'. The problem here is that when the container created the singleton, it injected its dependencies right in the beginning of its lifecycle, meaning that every access to the prototype dependency bean will always be to the same instance. If this dependency is a prototype scoped bean, this is not what you want.

To help you resolve these kind of issues, the controller offers method injection. Take a look:

<beans>
    <bean id="dependencyName" class="AClass" scope="prototype"/>
    <bean id="singletonBean" class="SingletonClass" scope="singleton">
        <lookup-method name="createDependency" bean="dependencyName"/>
    </bean>
</beans>

In this example, the singleton just uses createDependency() to get instances of the bean named "dependencyName". The container will (via AOP) take care of the rest ;)

Bean inheritance

Since version 1.1.0, Ding accepts bean inheritance via the 3 providers, xml, yaml, and annotations.

  <bean id="parentBean" abstract="true">
    <property name="someProperty"><ref bean="aDependencyBean"/></property>
    <aspect ref="aspectBean" type="method">
        <pointcut expression="^get.*" method="invoke"/>>
    </aspect>
  </bean>
  <bean id="childBean" parent="parentBean" class="MyBean" scope="singleton">
    <property name="someOtherProperty"><value>blah</value></property>
  </bean>

This code uses the "abstract" attribute to indicate that "parentBean" cant be instantiated, and it's just a template bean (this is not required, but recommended). The bean "childBean" is declared by using the attribute "parent" to specify the name of a bean that is used to inherit stuff from. Currently, you can inherit everything, and override the desired things explicitely.

Also, since 1.1.0, OOP inheritance is also supported, like so:

/**
 * @Component
 */
abstract class AParentClass
{
    /** @Resource */
    protected $someProperty;
    public function setSomeProperty($value)
    {
        $this->someProperty = $value;
    }

    public function getSomeProperty()
    {
        return $this->someProperty;
    }
}

/**
 * @Component
 */
class AParentClass2 extends AParentClass
{
    /** @Resource */
    protected $someOtherProperty;
    public function setSomeOtherProperty($value)
    {
        $this->someOtherProperty = $value;
    }

    public function getSomeOtherProperty()
    {
        return $this->someOtherProperty;
    }
}

/**
 * This is our bean.
 * @Component(name=childBean)
 * @Scope(value=singleton)
 */
class MyBean extends AParentClass2
{
    public function __construct()
    {

    }
}

When creating the bean "childBean", it will have injected all the properties specified in the parent classes ("someProperty" and "someOtherProperty").

Full examples:

Wiring By Type

The annotations driver supports the @Inject annotation, that can be used in properties, methods, and constructor arguments.

The full example is available here.

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject(type="IUserRepository")
     */
    protected $repository;
}

Since php has dynamic typing, you have to specify a class name (or interface) in the "type" argument of the annotation. In this case, if a bean that implements IUserRepository is found, it will be injected.

If multiple beans are found (or none at all), an InjectByTypeException is thrown, because Ding wont be able to choose between any of them. You can specify a bean as a "primary" candidate to avoid throwing this exception and tell the container which is the preferred bean to choose:

interface IUserRepository
{
}

/**
 * @Component
 * @Primary
 */
class AUserRepository implements IUserRepository
{
}

/**
 * @Component
 */
class AnotherUserRepository implements IUserRepository
{
}

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject(type="IUserRepository")
     */
    protected $repository;
}

In this case, the bean of class AUserRepository will be chosen to be injected into the UserService bean. You can also use @Primary for @Bean methods, and can also specify it as @Bean(primary="true").

If you are using xml:

<beans>
  <bean id="aUserRepository" class="AUserRepository" primary="true"/>
  <bean id="anotherUserRepository" class="AUserRepository"/>
</beans>

If you are using yaml:

beans:
  aUserRepository:
    class: AUserRepository
    primary: true
  anotherUserRepository:
    class: AnotherUserRepository

You can also specify a bean name to be chosen when multiple candidates are available, by using the @Named annotation, for properties and methods:

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject(type="IUserRepository")
     * @Named(name="iPrefferThisBeanImplementation")
     */
    protected $repository;
}

If you want to inject ALL beans of a given type, just specify the "type" as an array, like:

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject(type="IUserRepository[]")
     */
    protected $repositories;
}

In this case, all instances will be injected as an array of beans.

By default, all injected by type properties are required, so an InjectByTypeException will be thrown if no candidates are found to inject. If you dont mind not having anything to inject, you can specify "required=false":

interface IUserRepository
{
}

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject(type="IUserRepository[]", required="false")
     */
    protected $repositories;
}

So if no candidates are found, no exception will be thrown.

You can also use @Inject in methods. You can use any method name you like:

/**
 * @Component
 */
class UserService
{
    protected $repositories;

    /**
     * @Inject(type="IUserRepository[]")
     */
    public function initializeRepositories(array $repositories)
    {
        $this->repositories = $repositories;
    }
}

Since php does not allow you to specify an array of objects for a given class, you need to set the parameter type as "array", and specify the type of the objects to be injected in the "type" argument of the Inject annotation. But if you want to inject a single object, this will suffice:

/**
 * @Component
 */
class UserService
{
    protected $repository;

    /**
     * @Inject
     */
    public function setRepository(IUserRepository $repository)
    {
        $this->repository = $repository;
    }
}

@Inject can also be used in constructors:

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject
     */
    public function __construct(IUserRepository $userRepository, IScheduleRepository, $scheduleRepository)
    {
    }
}

If one of the constructor arguments has no defined type, it will be not eligible for injection by type, and will be skipped. In this case, you can mix it with @Value:

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject
     * @Value(name="otherArgument", value="${user.property}")
     */
    public function __construct(IUserRepository $repository, $otherArgument)
    {
    }
}

If you need to mix array AND scalar constructor injection with @Inject:

/**
 * @Component
 */
class UserService
{
    /**
     * @Inject
     * @Inject(name="otherArgument", type="IUserRepository[]")
     */
    public function __construct(IUserRepository $repository, $otherArgument)
    {
    }
}

To use @Named in constructor arguments, you need to specify the name of the parameter you are referring to. In the following example, the container will try to inject a IUserRepository component into the $repository argument. If multiple candidates are found, the @Named will choose make the container choose the bean with the name "preferredBeanName" for the argument $repository:

/**
 * @Component
 */
class UserService
{
/**
 * @Component
 */
class UserService
{
    /**
     * @Inject(name="repository", type="IUserRepository")
     * @Named(name="preferredBeanName", arg="repository")
     */
    public function __construct($repository)
    {
    }
}

You can also annotate @Bean methods:

/**
 * @Configuration
 */
class ServicesConfiguration
{
    /**
     * @Inject
     * @Bean
     * @Value(name="otherArgument", value="${user.property}")
     */
    public function userService(IUserRepository $repository, $otherArgument)
    {
        return new UserService($repository, $otherArgument);
    }
}

Annotations metadata

  • Name:The name of the annotation (i.e: @InitMethod).
  • Arguments:The annotations arguments. For example @InitMethod(method=xxx) will result in an annotation named InitMethod with an argument 'method' having the value 'xxx'.

Container

The container is a factory of beans. It constructs an instance of a bean using the metadata associated with it (and its subparts, like properties, annotations, etc).

Handling errors

The container does a set_error_handler() when it bootstraps. Whenever the phpvm calls this handler with an error (or warning, or notice, or whatever), ding will dispatch the event "dingError", so any bean(s) listening for this event will receive it as an ErrorInfo.

An example is here.

Handling signals

On SAPI's cgi and cli, it might be needed to handle posix signals. The container uses the pcntl extension to catch signals SIGQUIT, SIGHUP, SIGINT, SIGCHLD, SIGTERM, SIGUSR1, SIGUSR2 when it bootstraps. Whenever a signal is received, ding will dispatch the event "dingSignal", passing as argument the signal number, so any bean(s) listening for this event will receive it.

An example is here.

Handling shutdown

The container does a register_shutdown_function() when it bootstraps. When the phpvm calls this handler, ding will dispatch the event "dingShutdown", so any bean(s) listening for this event will receive it.

An example is here.

Aware Interfaces

  • Ding\Container\IContainerAware: Whenever ding is going to instantiate a bean whose class implements IContainerAware interface, it will inject the container instace to this bean
  • Ding\Bean\IBeanNameAware: Whenever ding is going to instantiate a bean whose class implements IBeanNameAware interface, it will inject the name of the bean
  • Ding\Bean\IAspectManagerAware: Whenever ding is going to instantiate a bean whose class implements IAspectManagerAware interface, it will inject current instance of the aspect manager (giving the ability to inject pointcuts, aspects, and general aop management).
  • Ding\Bean\IMessageSourceAware: Whenever ding is going to instantiate a bean whose class implements IMessageSourceAware interface, it will inject current instance of the message source (providing i18n capabilities).
  • Ding\Bean\IResourceLoaderAware: Whenever ding is going to instantiate a bean whose class implements IResourceLoaderAware interface, it will inject the instance of the resource loader in use, which is usually the container itself.
  • Ding\Logger\ILoggerAware: Whenever ding is going to instantiate a bean whose class implements ILoggerAware interface, it will inject the instance of the logger (log4php) currently in use by the container, so you can log there. The logger will have the name of the class (where '\' are replaced by '.')
  • Ding\Reflection\IReflectionFactoryAware: Whenever ding is going to instantiate a bean whose class implements IReflectionFactoryAware interface, it will inject the instance of ReflectionFactory in use, so you can access annotations, etc.

Extension points

If you're wondering about how to extend Ding with different or new features, there are some predefined "kind" of extension points:

  • Drivers
  • Bean definition Providers
  • Aspect/Pointcut definition providers
  • Helpers

Drivers

Drivers are normal "lifecyclers". These can be any object that implements ILifecycleListener and listens on one or more of the container lifecycle events: These classes usually reside in the namespace Ding\Bean\Factory\Driver.

  • AfterConfig
  • BeforeDefinition:
  • AfterDefinition
  • BeforeCreate
  • AfterCreate
  • BeforeAssemble
  • AfterAssemble

Each lifecycle event triggers a chain of the lifecyclers for that particular event, so the order is important.

Just like an aware interface, whenever ding instantiates a bean of a class that implements any of: the bean will be registered in the given lifecycle points. See docs/examples/lifecycle. This will allow you to extend the container functionality as much as you like.

  • Ding\Bean\Lifecycle\IAfterConfigListener
  • Ding\Bean\Lifecycle\IBeforeDefinitionListener
  • Ding\Bean\Lifecycle\IAfterDefinitionListener
  • Ding\Bean\Lifecycle\IBeforeCreateListener
  • Ding\Bean\Lifecycle\IAfterCreateListener
  • Ding\Bean\Lifecycle\IBeforeAssembleListener
  • Ding\Bean\Lifecycle\IAfterAssembleListener

Helpers

Helpers reside in the namespace Ding\Helpers. The helpers are usually normal beans, lets call them "user space" code (meaning that they are not part of Ding's architecture per se, unlike Drivers that are active players in the bean creation process).

Bean Definition Providers

If you need to provider bean definitions, implement IBeanDefinitionProvider. Whenever a bean is created that implements this interface, it is automatically registered as a bean definition provider and will be called by the container whenever necessary.

Aspect/Pointcut Definition Providers

If you need to provider aspect and pointcuts definitions, implement IAspectProvider and/or IPointcutProvider. Whenever a bean is created that implements this interface, it is automatically registered by the container into the AspectManager, and will be called whenever necessary.

ReflectionFactory

The namespace Ding\Reflection contains ReflectionFactory. This factory is used throughout the container and its extension points.

Back

3. Events and event driven programming support


Events are supported by the container itself. This means that you can register as many events and as many listeners per events as you need. This is useful so you can decouple your code a little more (i.e: updating some entity would trigger accounting events, that are listened to by some other beans that may be responsible to propagate this events to other systems).

The container supports triggering and listening for events, via 2 methods:

    public function eventListen($eventName, $beanName);
    public function eventDispatch($eventName, $data = null);

So you may programatically register a new listener by issuing:

$container->eventListen('someEvent', 'myService');

And then you can trigger it (also programatically) with:

$container->eventDispatch('someEvent', $entity);

The event is a string (as you can see), and also the handler is passed with a bean name (also a string). These handlers will only be instantiated when needed (i.e: the event has been triggered). The convention is that the container will look for a method name "onSomeEvent()" and call it with $entity.

You can also declare the beans that listen for particular events right in the container configuration.

Using the xml provider

<beans>
  <bean id="bean" class="Bean" scope="singleton" listens-on="bean2Created"/>
</beans>

The "listens-on" value is a csv string of events, lower camel cased.

Using the yaml provider

beans: !!map
  bean3:
    class: Bean3
    scope: singleton
    listens-on: bean2Created

The "listens-on" value is a csv string of events, lower camel cased.

Using the annotations provider

/**
 * @Configuration
 */
class Config
{
    /**
     * @Bean(class=Bean4)
     * @Scope(value=singleton)
     * @ListensOn(value=bean2Created)
     */
    public function someBean()
    {
        return new Bean4;
    }
}

Since 1.1.3, events are supported in @Component and @Bean. If you want to specify multiple events, you can do so in this way:

/**
 * @Component
 * @ListensOn(value={bean2Created, anotherEvent})
 */
class SomeComponent
{
...
}

/**
 * @Configuration
 */
class Config
{
    /**
     * @Bean(class=Bean4)
     * @Scope(value=singleton)
     * @ListensOn(value={bean2Created, anotherEvent})
     */
    public function someBean()
    {
        return new Bean4;
    }
}

Predefined events in the container

Ding defines the following events by default:

  • dingError: Triggered whenever php triggers an error, notice, etc.
  • dingShutdown: Triggered when php is shutting down.
  • dingSignal: Available for SAPI's cli and cgi. Triggered when the container catches a posix signal.

Back

4. Using Resources


Resources are just a small abstraction level above the native stream implementation of php. For examples see docs/examples/resources.

You can get resources directly from the container. Since the container implements Ding\Resource\IResourceLoader, you can call getStream($path) on it. You can also use resources by yourself, without involving the container.

A resource implements Ding\Resource\IResource.

Available implementations

  • FilesystemResource: Can be used to get resources for either relative or absolute paths. Can be used with or without the scheme file://
  • IncludePathResource: Can be used to get resources inside the include_path. Can be used with or without the scheme file:// and includepath://
  • URLResource: Directly access php supported scheme's. See: http://www.php.net/manual/en/wrappers.php
$resource = $container->getResource('includepath://somefile.php');

Or:

$resource = new IncludePathResource('somefile.php');

Some other examples:

$resource = $container->getResource('zip://file.zip#README.txt');
$resource = new URLResource('zip://file.zip#README.txt');
$resource = $container->getResource('file:///some/file/here.txt');
$resource = new FilesystemResource('file:///some/file/here.txt');

IResourceLoaderAware interface

Implement Ding\Resource\IResourceLoaderAware interface in your own beans and they will be automatically injected with the resource loader (the container) in use.

Resources as dependencies

You can also declare resources in your bean properties and constructor arguments, like:

<beans>
    <bean id="aBean" class="MyBeanClass" scope="singleton">
        <property name="aResource"><value>resource://${anUrl}</value></property>
        <property name="aResource2"><value>resource://includepath://config.php</value></property>
        <constructor-arg><value>resource://http://www.something.com</value></property>
    </bean>
</beans>

Ding will autodetect the resource:// prefix and return a property of type IResource instead of the string.

Back

5. Internationalization (i18n) support


Ding provides i18n functionality, so you can support multiple languages in your application.
see docs/examples/i18n for a quick example.

Suppose you want to ship your application in en_US and es_AR languages. Also, suppose your application can be divided into 3 big modules:

  • Customers
  • Products
  • Billing

You would need 3 files named like this somewhere in your include_path:

  • customers_es_AR.properties / customers_en_US.properties
  • products_es_AR.properties / products_en_US.properties
  • billing_es_AR.properties / billing_en_US.properties

Sample contents for the files:

customers_es_AR.properties:

message.example = "Hola! esto es un argumento {1}"

customers_en_US.properties:

message.example = "Hello! this is an argument {1}"

Configuring the container

Whenever ding finds a bean named "messageSource", it will create it and use it to resolve messages:

    <bean id="messageSource" class="Ding\MessageSource\Impl\MessageSourceImpl" scope="singleton">
       <property name="basenames">
           <array>
               <entry key="0"><value>customers</value></entry>
               <entry key="1"><value>products</value></entry>
               <entry key="2"><value>billing</value></entry>
           </array>
       </property>
    </bean>

A MessageSourceImpl implements the interface IMessageSource:

interface IMessageSource
{
    public function getMessage($bundle, $message, array $arguments, $locale = 'default');
}

The container implements this interface, so you can call

echo $container->getMessage('products', 'message.example', array('anArgument'), 'en_US');

Or:

echo $container->getMessage('products', 'message.example', array('anArgument'));

And the messages will be looked for in the products_default.properties file.

IMessageSourceAware

Each bean that implements the IMessageSourceAware interface, will be autoinjected with the message source in use (the container).

interface IMessageSourceAware
{
    public function setMessageSource(IMessageSource $messageSource);
}
Back

6. Aspect Oriented Programming


This article has a little background on AOP using Ding: here

Ding has an implementation of the interceptor pattern, which is used to bring aop in the framework. Currently, there are 2 available kind of interceptors, both of them will call all aspects in a chained fashion. Either of those chained aspects can break or continue the chain by choosing to (or not to) call the proceed() method.

Since 1.5.1 the proceed() method supports arguments. All the arguments passed to proceed() will override the arguments used to call the original method.

  • Method: Called when the method is called.
  • Exception: Called when the called method has thrown an exception not catched.

The aspects are declared in the xml provider like this (yaml is supported, please see: docs/examples/yaml for an example):

<bean id="some" class="AClass" scope="prototype">
    <aspect ref="AnAspectBean" type="method">
        <pointcut expression="targetMethod" method="invoke"/>
    </aspect>
</bean>

The above example defines a bean named "some". When the container is requested to return an instance of this bean, it will notice that is has an aspect attached, so the bean's class is automatically proxied and the instance of that proxy is what is really returned.

In this case, the aspect references another bean that should also be known to the container. That bean must have a method named like in the method attribute, which is to be called.

NOTE: Since 1.1.0, when using bean inheritance, aspects specified on parent beans will also be applied to children beans that inherit from this parent.

Notice the "type" attribute in the aspect tag. The other possible value for this attribute is "exception", this will attach an ExceptionInterceptor instead of a MethodInterceptor.

Whenever a call is made to the method defined in the pointcut tag (in this case the method is called targetMethod), the method on your bean will be called. The same applies to exception interceptors.

You can also use regular expressions to define a pointcut for more than one method. The syntax for the regular expression is the same as preg_match(), please see the php manual page for this function for details. Example:

<bean id="some" class="AClass" scope="prototype">
    <aspect ref="AnAspectBean" type="method">
        <pointcut expression="^get$" method="invoke"/>
    </aspect>
</bean>

Sharing pointcuts (aka global pointcuts)

You can define global pointcuts and use them in your aspect definitions:

<pointcut id="aPointcut" expression="^get$"/>
<bean id="some" class="AClass" scope="prototype">
    <aspect ref="AnAspectBean" type="method">
        <pointcut pointcut-ref="aPointcut" method="invoke"/>
    </aspect>
</bean>

Global aspects

You can define global aspects using regular expressions for classes and references to global pointcuts (which can also use regular expressions):

<pointcut id="aPointcut" expression="^get$"/>
<aspect id="anAspect" expression="Service$" ref="AnAspectBean" type="method">
  <pointcut pointcut-ref="aPointcut"/>
</aspect>

In the above example, the expression attribute of the aspect declaration is applied to the class names for the beans. If the name of the class is matched, then the pointcuts expressions are tried.

NOTE: Since 1.1.0, Ding will apply global aspects applied to parent classes as well, so if you apply a global aspect to a class-expression that matches with a parent class of a bean, the bean will have that aspect applied.

So you can define pointcuts inside aspects or globally. You can also define aspects in beans or globally. The aspects can have references to global pointcuts or the pointcut definitions themselves.

Using annotations

You may annotate any of your classes with @Aspect, this will tell Ding to scan the methods of this class and look for @ExceptionInterceptor and @MethodInterceptor:

/**
 * @Aspect
 */
class AspectA
{
    /**
     * @MethodInterceptor(class-expression=C.+,expression=g.+)
     * @ExceptionInterceptor(class-expression=C.+,expression=getB)
     */
    public function invoke(MethodInvocation $invocation)
    {
        try
        {
            echo "Before: " . $invocation->getOriginalInvocation() . "\n";
            $invocation->proceed('b', 'c', 'd');
            echo "After\n";
        } catch(Exception $e) {
            echo "Move along, nothing happened here.. \n";
        }
    }

    public function __construct()
    {
    }
}

Example

Back

7. Configuring the container


The container::getInstance() method accepts an array that is used configure the framework.

$a = ContainerImpl::getInstance($properties);

Logging

Use the optional property:

$properties['ding']['log4php.properties'] => './log4php.properties'

Bean Factory Providers

You may configure any of the bean definition providers available:

  • xml: Parses the given XML file(s), usually beans.xml and get bean definitions from there. Requires the property 'filename'.
  • yaml: Parses the given YAML file(s), usually beans.yaml and get bean definitions from there. Requires the property 'filename'.
  • annotation: Extracts bean definitions and other information right from the annotated code. Requires the property 'scanDir'.

These providers are configured with their own properties:

  • $properties['ding']['factory']['bdef']['xml']
  • $properties['ding']['factory']['bdef']['yaml']
  • $properties['ding']['factory']['bdef']['annotation']

Example:

$properties['ding']['factory']['bdef']['xml'] = array(
    'filename' => 'beans.xml'
)

$properties['ding']['factory']['bdef']['annotation'] = array(
    'scanDir' => array(__DIR__)
)

You can also specify multiple files, for either xml or yaml providers, like:

$properties['ding']['factory']['bdef']['xml'] = array(
    'filename' => array('beans.xml', 'otherBeans.xml')
)

$properties['ding']['factory']['bdef']['yaml'] = array(
    'filename' => array('beans.yaml', 'otherBeans.yaml')
)

You can also specify multiple search directories (paths), for either xml or yaml providers, like:

$properties['ding']['factory']['bdef']['yaml'] = array(
    'filename' => array('beans.yaml', 'otherBeans.yaml'), 'directories' => array('/etc/Ding', '/usr/local/etc/Ding')
)

This will tell the yaml provider (you can also use it with the xml provider) to look for the bean files beans.yaml and otherBeans.yaml in the directories specified. The order of the directories is important, so the file used will be the first available one.

User properties

Use the optional property: $properties['ding']['properties'] to specify any key/value pair that you may want search and replace via the PropertyFilter. Example:

<beans>
    <bean id="beanName" class="A\B\C" scope="prototype">
        <property name="someProperty">
            <value>${some.value}</value>
        </property>
    </bean>
</beans>
$properties['ding']['factory']['properties'] = array(
    'some.value' => 'This is the final property value'
)

NOTE: If a property starts with "php.", like "php.date.timezone", ding will automatically call ini_set() for that property, like: ini_set('date.timezone', value). See docs/examples/PhpProperties.

You can also configure the PropertiesHolder bean to inject properties files:

<beans>
   <bean id="PropertiesHolder" class="Ding\Helpers\Properties\PropertiesHelper" scope="singleton">
       <property name="locations">
         <array>
           <entry key="0"><value>resource://includepath://file.properties</value></entry>
           <entry key="1"><value>other.properties</value></entry>
           <entry key="2"><value>file://./user.properties</value></entry>
           <entry key="3"><value>file://./php.properties</value></entry>
           <entry key="4"><value>file://${some.value}/php.properties</value></entry>
         </array>
       </property>
   </bean>
   <bean id="beanName" class="A\B\C" scope="prototype">
       <property name="someProperty">
           <value>${some.value}</value>
       </property>
    </bean>
</beans>

So you can have your own properties and the ones that affect php behavior (i.e: "php." properties) in your own separate config files.

Sample properties file:

some.value=value
php.some.php.property=value

You can even use properties inside the properties holder definition, like:

<beans>
   <bean id="PropertiesHolder" class="Ding\Helpers\Properties\PropertiesHelper" scope="singleton">
       <property name="locations">
         <array>
           <entry key="0"><value>resource://file://${config.dir}/file.properties</value></entry>
         </array>
       </property>
   </bean>
</beans>

The property config.dir must now be defined when instantiating the container:

$options = array('ding' => array(
    'factory' => array(
        'properties' => array('config.dir' => '/etc/config')
    )
));

When the properties holder is loaded, the property will complete the path for the file "file.properties".

Cache Subsystems

The cache subsystems use $properties['ding']['cache']. The subsystems are:

  • proxy: To cache proxy definitions: its expensive to create proxies because php lacks this hability, so we have to actually generate code. This code can be cached.
  • annotations: To cache annotations, its *really* expensive if you have a large source tree or use them a lot. So we cache the annotations for each class and the files and directories scanned (along with all autodiscovered classes there).
  • bdef: To cache BeanDefinitions so no xml's or annotations have to be reparsed per every request.
  • beans: To cache the beans themselves. This is actually not so easy as it sounds, so dont expect it to work as intended. Note: this is not a flaw of Ding itself but inherent to the architecture used generally in php, the lack of the concept of an application server, etc. It is recomended to use the dummy implementation for this cache.
  • aspect: To cache aspect and pointcut definitions (i.e: AspectManager).

The resulting properties needed to configure each one of the cache subsystems:

  • $properties['ding']['cache']['proxy']
  • $properties['ding']['cache']['bdef']
  • $properties['ding']['cache']['beans']
  • $properties['ding']['cache']['aspect']
  • $properties['ding']['cache']['annotations']

In turn, each one of these entries, require the 'impl' key to point to one of the possible cache implementations:

  • dummy: Does not cache at all.
  • file: Uses the filesystem. Requires the additional option 'directory'.
  • apc: Uses apc.
  • memcached: Uses memcached php extension. Requires additional option 'memcached', an array with: 'host' and 'port'.
  • zend: Uses Zend_Cache. Requires additional option: 'zend' with an array of options of your choice according to Zend_Cache documentation.

Then, you can configure any of the cache subsystems with any of the cache implementations:

$properties['ding']['cache']['proxy'] = array(
    'impl' => 'file', 'directory' => '/tmp/Ding/cache'
)

$properties['ding']['cache']['bdef'] = array(
    'impl' => 'memcached', 'memcached' => array('host' => '127.0.0.1', 'port' => 11211)
)

$properties['ding']['cache']['proxy'] = array(
    'impl' => 'apc'
)

$properties['ding']['cache']['aspect'] = array(
    'impl' => 'apc'
)

$properties['ding']['cache']['annotations'] = array(
    'impl' => 'apc'
)

$properties['ding']['cache']['bdef'] = array(
    'impl' => 'dummy'
)
$zendCacheOptions = array(
    'frontend' => 'Core',
    'backend' => 'File',
    'backendoptions' => array('cache_dir' => '/tmp/Ding/zend/cache'),
    'frontendoptions' => array('lifetime' => 10000, 'automatic_serialization' => true)
);

$properties['ding']['cache']['bdef'] = array(
    'impl' => 'zend', 'zend' => $zendCacheOptions
)
Back

8. The MVC Implementation


Classes

  • Ding\Mvc\Action
  • Ding\Mvc\Dispatcher
  • Ding\Mvc\ModelAndView
  • Ding\Mvc\RedirectModelAndView
  • Ding\Mvc\ForwardModelAndView
  • Ding\Mvc\View
  • Ding\Mvc\Htt

Interfaces

  • Ding\Mvc\IMapper
  • Ding\Mvc\IViewResolver
  • Ding\Mvc\IViewRender
  • Ding\Mvc\IHandlerInterceptor

Description

The MVC implementation of Ding is in the namespace Ding\Mvc. The components are:

  • Actions: Actions are strings, that uniquely identifies an action (usually triggered with a request) in your software. Actions can have arguments, which are nothing more than key/value pairs.
  • Mapper: The mapper knows and returns the controller that can handle the given Action name.
  • Dispatcher: The dispatcher is the responsible for calling the right controller with the right Action object.
  • ModelAndView: This is the result of the execution of a controller. A controller must return a ModelAndView object containing the name of the view to render and other optional data.
  • TwigViewRender: The view is processed by Twig
  • SmartyViewRender: The view is processed by Smarty
  • HttpViewRender: The view is processed by php via include_once.
  • ForwardModelAndView: This is a special ModelAndView that has the meaning of calling again the dispatcher with a new given action and new or same arguments.
  • RedirectModelAndView: This is a special ModelAndView that has the meaning of redirecting the request to somewhere else (i.e: http 302 header).
  • ViewResolver: The ViewResolver knows how to get a ViewResolver based on the given View name from the ModelAndView object.
  • View: Views are in charge of rendering the ModelAndView in a proper way for the actor that triggered the request.
  • Interceptor: Classes that implement IHandlerInterceptor can be used as pre and post interceptors. The dispatcher will chain calls to preHandle() and postHandle(). Returning false or a ModelAndView will interrupt the chain, while returning true will continue executing it.

HTTP implementation Classes

  • Ding\Mvc\Http\HttpAction
  • Ding\Mvc\Http\HttpDispatcher
  • Ding\Mvc\Http\HttpExceptionMapper
  • Ding\Mvc\Http\HttpView
  • Ding\Mvc\Http\HttpViewResolver
  • Ding\Mvc\Http\HttpViewRender
  • Ding\Mvc\Http\TwigViewRender
  • Ding\Mvc\Http\SmartyViewRender
  • Ding\Mvc\Http\HttpUrlMapper
  • Ding\Mvc\Http\HttpFrontController
  • Ding\Mvc\Http\HttpInterceptor

Annotations

  • @Controller
  • @RequestMapping(url=xxx)

Both of these annotations need to be present in a controller defined by annotations.
@Controller will tell the mvc driver to map the class with the content of the url param in the @RequestMapping annotation. This is equivalent to configure the class as a bean in beans.xml and mapping there the url to that bean.

Since 1.1.5, @RequestMapping will also accept an array of strings, example:

  • @RequestMapping(url={/xxx, /yyy, /zzz})
  • In the case of http, the following components exist:

    • HttpAction: The HttpAction encapsulated the uri, the method used, and the arguments.
    • HttpDispatcher: Dispatches the request, using the mapper and the view resolver to render the view. The controller are mapped with both method and uri's. The action names, however, are invoked by convention. Suppose the uri /MyController/List, will mean that the controller that handles /MyController, needs to have a method named ListAction.
    • HttpView: Render the given view (a file in the filesystem), outputing headers and data.
    • HttpViewResolver: Maps a view name with a file in the filesystem. The root directory, an optional prefix and suffix can be configured.
    • HttpUrlMapper: Maps an uri and its method to a controller.
    • HttpFrontController: This is intended to be instantiated and called from your own entry point, will load the container and also, invoke the dispatcher and completely handle the request.
    • HttpInterceptor: This class can be extended, thus quickly creating pre and post dispatch interceptors.

    A special setup is required in the web server. You need to forward ALL uri's (at least the ones you're interested in manage with Ding) to your entry point, let's say index.php. In lighttpd you would do:

    url.rewrite-once = ( "^/(.*)" => "/index.php" )

    And you're index.php would be something like this:

    HttpFrontController::handle($properties, '/Some/Mapped/Path');

    And that's it. Take a look at the example below to know how to actually configure the HttpViewResolver and HttpUrlMapper in the container.

    Http Example

    Back

    9. The Syslog helper


    This helper is designed to be a bean of scope singleton, and a facade for using the syslog in unix environments.

    Class

    • Ding\Helpers\Syslog\SyslogHelper

    Description

    See the api and example for more details. You have to configure the known syslog properties "ident", "options", and "facility".

    Example

    Back

    10. The PAMI helper


    This helper is designed to be a bean of scope singleton that uses the PAMI framework to let a listener of your choice be called with any asterisk manager events.

    Class

    • Ding\Helpers\Pami\PamiHelper

    Description

    This bean has the properties "handler", "host", "port", "username", "password", "readTimeout", and "connectTimeout". Handler is a reference to another bean that must implement Ding\Helpers\Pami\IPamiEventHandler.

    Example

    See AsterTrace.

    Also, you might want to look at this article.

    Back

    11. The PAGI helper


    This helper is designed to be a bean of scope singleton that uses the PAGI framework, so you can construct telephony applications.

    Class

    • Ding\Helpers\Pagi\PagiHelper

    Example

    Back

    12. The TCP Client helper


    Class

    • Ding\Helpers\Tcp\TcpClientHelper

    Description

    This helper is intended to help you write tcp clients by just implementing the Ding\Helpers\Tcp\ITcpClientHandler interface. You will need to implement the following methods:

    • connectTimeout(): Called when the connection has not been established in less than the configured milliseconds.
    • readTimeout(): Called when the configured maximum read timeout has been reached with less than the configured minimum bytes to read.
    • beforeConnect(): Called just before trying to connect to the server.
    • connect(): Called when the connection has been established successfully.
    • disconnect(): Called when either of the peers (you or the server) closes the connection.
    • data(): Called when data is ready to be consumed from the server.

    The helper itself has some useful method to control its behavior:

    • open(): Opens the connection. You may optionally pass the ip address and port number to use to make the connection from. (i.e: 1.1.1.1:80 will try to make the connection FROM that address).
    • read(): Reads data from the server (usually used from the data() method above).
    • write(): Sends data to the server.
    • close(): Closes the connection.
    • process(): This is supposed to be called automatically if you declare(ticks=1). The helper uses register_tick_function() to achieve this. If you dont want to declare(ticks=1), you need to call this method from your own main loop. It will select() the socket, check for data available, control timeouts, etc.

    See the php doc API for more details.

    Example

    Back

    13. The TCP Server helper


    Class

    • Ding\Helpers\Tcp\TcpServerHelper
    • Ding\Helpers\Tcp\TcpPeer

    Description

    Use this helper to create one or more tcp server by just implementing the interface Ding\Helpers\Tcp\ITcpServerHandler. You will need to implement the following methods:

    • beforeOpen(): Called right before opening the socket.
    • beforeListen(): Called right before start listening in the socket for new connections.
    • close(): Called right after the server socket has ben closed.
    • handleConnection(): Called when a new connection has been established, will receive a TcpPeer as a client.
    • handleData(): Called when a client has sent data, will receive a TCPPeer as the client.
    • disconnect(): Called when a client disconnects, will receive a TCPPeer as the client.
    • readTimeout(): Called when the read timeout has expired on a client, will receive a TcpPeer as a client.

    The helper itself has some useful method to control its behavior:

    • open(): Opens the server socket.
    • close(): Closes the server socket.
    • process(): This is supposed to be called automatically if you declare(ticks=1). The helper uses register_tick_function() to achieve this. If you dont want to declare(ticks=1), you need to call this method from your own main loop. It will select() the socket, check for data available, control timeouts, etc.

    To control a peer, you can call these methods on the TcpPeer:

    • disconnect(): Disconnects the peer.
    • read(): Reads data from the peer.
    • write(): Writes data to the peer.

    Example

    Back