Skip to content

Commit

Permalink
Finishing the documentation for Open Power Collector.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyxist committed Mar 9, 2011
1 parent c45e002 commit 44bd0ff
Show file tree
Hide file tree
Showing 5 changed files with 333 additions and 5 deletions.
97 changes: 96 additions & 1 deletion input/en/collector.available.configuration.txt
Expand Up @@ -2,4 +2,99 @@ Title: Configuration

----

Configuration.
The configuration loaders allow to load the data from the various types of files. The currently supported formats are:

* INI files
* XML files (through the SimpleXML extension)
* YAML files (through the Symfony 2 YAML Component)

The loaders extend a common `Opl\Collector\Configuration\FileLoader` class which provides a unified interface for specifying paths and files to load. The list of sequentially scanned paths is provided in the constructor as an array:

~~~~
[php]
$loader = new IniFileLoader(array(
'./directory1/',
'./directory2/',
'./directory3/',
));
~~~~

If we have a single path, we can omit the array and pass the string as an argument.

The file to load is specified with the `setFile()` method, and to get the name of the current file, we can use `getFile()` respecitvely:

~~~~
[php]
$loader->setFile('file.ini');
~~~~

The method implements fluent interface:

~~~~
[php]
$collector->loadFromLoader(Collector::ROOT, $loader->setFile('file.xml'));
~~~~

> [information]
> Loading the data from configuration files takes a while. In order to optimize the configuration loading, you can combine your collector with Open Power Cache.

IniFileLoader
=============

This loader loads the data from INI files. Each line defines a single option, and the key is the fully qualified path:

~~~~
[ini]
application.name = "My Application"
application.database.dbtype = "mysql"
application.database.dbname = "foo"
application.database.host = "localhost"
application.database.dbuser = "root"
application.database.dbpass = "root"
~~~~

XmlFileLoader
=============

This loader loads the data from XML files. The XSD Schema is provided with the installation package in the `xml/collector.xsd` file. Below, you can find a sample XML structure:

~~~~
[xml]
<?xml version="1.0" encoding="UTF-8"?>
<group
xmlns="http://xml.invenzzia.org/opl/collection"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="./xml/collection.xsd">
<group name="application">
<item name="name">My application</item>
<group name="database">
<item name="dbtype">mysql</item>
<item name="dbname">foo</item>
<item name="host">localhost</item>
<item name="dbuser">root</item>
<item name="dbpass">root</item>
</group>
</group>
</group>
~~~~

The first `<group>` tag represents the root node and does not have a name. For the rest of the tags, providing the `name` attribute is obligatory. Each `<group>` tag may contain any number of both `<group>` and `<item>` tags in any order.

For the XML document validation, we can use the `xmlint` application.

YamlFileLoader
==============

This loader loads the data from YAML files. It simply returns the array produced by Symfony 2 YAML Component parser:

~~~~
[yml]
application:
name: 'My application'
database:
dbtype: 'mysql'
dbname: 'foo'
host: 'localhost'
dbuser: 'root'
dbpass: 'root'
~~~~
21 changes: 20 additions & 1 deletion input/en/collector.available.visit.txt
Expand Up @@ -2,4 +2,23 @@ Title: Visit

----

Visit
The visit loaders collect various data about the user visit.

HostLoader
----------

Returns the data about the remote host. The registered keys:

* `ip` - the remote IP address
* `binaryIp` - the binary form of the remote IP address (4 or 16 bytes, depending on the IP version)
* `ipVersion` - either 4 or 6.

ConnectionLoader
----------------

Returns the data about the connection. The registered keys:

* `port` - the connection port
* `isSecure` - **true**, if this is a secure SSL connection
* `method` - the HTTP request method name
* `protocol` - the protocol name. Currently the loader recognizes the following protocols: `https`, `http` and `wap`.
26 changes: 25 additions & 1 deletion input/en/collector.caching.txt
Expand Up @@ -2,4 +2,28 @@ Title: Caching

----

Caching.
In some usage scenarios such as the configuration system, loading the data from the files every request is very inefficient. In this case we would like to cache the configuration data tree in a shared memory. Open Power Collector can be integrated with Open Power Cache in order to provide a caching service. The example below shows, how to use the `Collector` objects with APC:

~~~~
[php]
use Opl\Cache\APC;
use Opl\Collector\Collector;
use Opl\Collector\Configuration\IniFileLoader;

$apc = new APC(array(
'prefix' => 'myapp:',
'lifetime' => 86400
));

$collector = new Collector($apc, 'configuration');
if(!$collector->isCached())
{
$loader = new IniFileLoader('./config/');
$collector->loadFromLoader(Collector:ROOT, $loader->setFile('config.ini'));
$collector->save();
}

echo $collector->get('some.key');
~~~~

The second argument of the class constructor specifies the cache key, where the data should be stored. To check if the data are successfully loaded from a cache, we use `isCached()` method and to populate the cache again - `save()`. Note that this gives you a control over what data are cached and what are not. You can safely load part of the data from the cache, and generate the rest dynamically.
57 changes: 56 additions & 1 deletion input/en/collector.custom-loaders.txt
Expand Up @@ -2,4 +2,59 @@ Title: Writing custom loaders

----

Writing custom loaders.
In order to write a custom loader, you must implement the `Opl\Collector\LoaderInterface`:

~~~~
[php]
interface LoaderInterface
{
public function import();
} // end LoaderInterface;
~~~~

The only method is `import()` which must collect the data and return them in a form of an associative array. The array can be nested to get a hierarchical structure:

~~~~
[php]
class StaticLoader implements LoaderInterface
{
public function import()
{
return array(
'user' => array(
'id' => 5,
'login' => 'John Doe',
'email' => 'john@example.com'
),
'session' => array(
'id' => 'foo',
'lastActivity' => 123456789
)
);
} // end import();
} // end StaticLoader;
~~~~

For loading the data from a file, we can utilize the `Opl\Collector\Configuration\FileLoader` abstract class which provides the features related to locating the files on the disk and managing the paths. Below you can find, how to begin your `import()` method in this case:

~~~~
[php]
class MyFileLoader extends FileLoader
{
public function import()
{
if(null === $this->currentFile)
{
throw new BadMethodCallException('Cannot load a file: no file specified');
}

$data = load_the_file($this->findFile($this->currentFile));

// process the data here

return $dataArray;
} // end import();
} // end MyFileLoader;
~~~~

The `findFile()` method scans all the defined paths, looking for the first occurence of the specified file. If it finds one, it returns the complete path to the file that should be loaded. If no file is found, it throws an exception. Note that it is a good practice to check if the programmer actually set any file name, just like we have done this in the first three lines.
137 changes: 136 additions & 1 deletion input/en/collector.theory-of-operation.txt
Expand Up @@ -2,4 +2,139 @@ Title: Theory of operation

----

Theory of operation.
Open Power Collector offers simple yet effective way for retrieving the data from various sources. There are three primary concepts upon which the library is built:

* **Loaders** - small classes that know, where the data are stored and how to load them into the system.
* **Providers** - classes that know how to share the data with the script.
* **Collectors** - extended providers with the ability to load the data with the loaders.

Loaders
=======

A valid loader must extend the `Opl\Collector\LoaderInterface` class. It provides a single `import()` method. Its task is to produce a nested array of keys and their values. Below, you can see a sample implementation that returns some static data:

~~~~
[php]
class StaticLoader implements LoaderInterface
{
public function import()
{
return array(
'user' => array(
'id' => 5,
'login' => 'John Doe',
'email' => 'john@example.com'
),
'session' => array(
'id' => 'foo',
'lastActivity' => 123456789
)
);
} // end import();
} // end StaticLoader;
~~~~

There are no limits on the nesting level other than the PHP and system resource limits.

Loaders can load the data from everywhere. As you can see in the next chapters, they are used to build a configuration system thanks to a couple of loaders that load the XML, YAML or INI files. On the other hand, the `Visit` namespace collects various information about the HTTP request, and in the example above, we showed that they can also return session data. The whole library can be seen as a big data storage that offers them in the unified structure through a unified interface, no matter what those data are.

Collectors
==========

The `Collector` class provides the interface to collect and access the data. It organizes them into a hierarchical tree of keys and their values. Each value is identified by a path of node names separated with a dot, such as `foo.bar.joe`. We can access both the scalar values, and the whole groups of nodes. For example, let's say that `application.database.host` keeps the database host setting. In simple applications, we can simply access each of the database connection properties explicitely, but what if the database connection is constructed by some factory? The collector allows you to pass the entire `application.database` group into the factory:

~~~~
[php]
use Opl\Collector\ProviderInterface;
use Opl\Collector\Collector;

$collector = new Collector();

function dbFactory(ProviderInterface $provider)
{
return new PDO(
$provider->get('dbtype').':'.
'host='.$provider->get('host').';'.
'dbname='.$provider->get('dbname'),
$provider->get('dbuser'),
$provider->get('dbpass')
);
} // end dbFactory();

$pdo = dbFactory($collector->get('application.database'));
~~~~

The data can be inserted into a collection from loaders or from arrays. In each case, we can specify the node, where the new keys should be mounted:

~~~~
[php]
$collector->loadFromArray(Collector::ROOT, array(
'foo' => 'value 1',
'bar' => 'value 2',
'joe' => array(
'goo' => 'value 3',
'hoo' => 'value 4'
)
));

// prints "value 1"
echo $collector->get('foo');
~~~~

In the example above, we mounted the new keys as root nodes, but there are also other possibilities:

~~~~
[php]
$collector->loadFromArray('application.database', array(
'dbtype' => 'mysql',
'dbname' => 'foo',
'host' => 'localhost',
'dbuser' => 'root',
'dbpass' => 'root'
));

// prints "mysql"
echo $collector->get('application.database.dbtype');
~~~~

Providers
=========

`` specifies the interface to access the data. The class `Provider` is the default data provider that organizes the values into a hierarchical tree. The plain provider class does not, however, contain anything to insert the data into the object, so we cannot construct it explicitely. In fact, `Provider` objects are returned by collectors, where we attempt to get a whole group of keys. Let's get back to our database example:

~~~~
[php]
use Opl\Collector\ProviderInterface;
use Opl\Collector\Collector;

$collector = new Collector();

function dbFactory(ProviderInterface $provider)
{
return new PDO(
$provider->get('dbtype').':'.
'host='.$provider->get('host').';'.
'dbname='.$provider->get('dbname'),
$provider->get('dbuser'),
$provider->get('dbpass')
);
} // end dbFactory();

$pdo = dbFactory($collector->get('application.database'));
~~~~

Here, the `$collector->get()` call constructs a provider object and internally initializes it to point to the `application.database` node. In this way, the factory can still use the same interface to retrieve the data and does not have to know the fully qualified path to the connection configuration. Because we do not need to modify the tree anymore, the providers simply offer a way to access the data and nothing more. The new nodes can be added only with a collector.

For simple unit testing, mocking or more specific needs, the `Opl\Collector\ProviderInterface` interface specifies, how to access the data:

~~~~
interface ProviderInterface
{
const THROW_EXCEPTION = 0;
const THROW_NULL = 1;

public function get($key, $errorReporting = self::THROW_EXCEPTION);
} // end ProviderInterface;
~~~~

As the first argument of the `get()` method, we specify the key we want to get, and as the second argument - the error reporting mode. The providers should throw exceptions, if the key do not exists and `$errorReporting` is set to `THROW_EXCEPTION`. `THROW_NULL` indicates that the `get()` method should return **null** in this case.

0 comments on commit 44bd0ff

Please sign in to comment.