<?php

namespace Sokil\Mongo\Migrator\Console\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Sokil\Mongo\Migrator\Console\Command;

class Create extends Command
{
    protected function configure()
    {
        $this
            ->setName('create')
            ->setDescription('Create new migration')
            ->addArgument(
                'name',
                InputArgument::REQUIRED,
                'Name of migration in CamelCase notation'
            )
            ->setHelp('Create new migration');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     *
     * @return int|null|void
     *
     * @throws \Exception
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $className = $input->getArgument('name');
        if (!$className) {
            throw new \Exception('Name not specified');
        }

        if (!preg_match('/^([A-Z][a-z0-9]+)+$/', $className)) {
            throw new \Exception('Name must be in CamelCase notation');
        }

        $migrationFilename = date('YmdHis') . '_' . $className . '.php';
        $migrationFileDir = $this->getManager()->getMigrationsDir();
        $migrationFilePath =  $migrationFileDir . '/' . $migrationFilename;

        if (file_exists($migrationFilePath)) {
            throw new \Exception('Migration file with same name already exists');
        }

        if (!is_writeable(dirname($migrationFilePath))) {
            throw new \Exception('Permission denied for writting to ' . $migrationFilePath);
        }

        // create migrations file
        $migrationFileContent = str_replace(
            '{{MIGRATION_CLASSNAME}}',
            $className,
            file_get_contents(__DIR__ . '/../../../templates/MigrationTemplate.php.dist')
        );

        file_put_contents($migrationFilePath, $migrationFileContent);

        // show result
        $output->writeln('New migration created at ' . $migrationFilePath);
    }
}


<?php

namespace Sokil\Mongo\Migrator;

use Sokil\Mongo\Collection;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Sokil\Mongo\Client;
use Sokil\Mongo\Migrator\Event\ApplyRevisionEvent;

/**
 * Migration management
 */
class Manager
{
    /**
     * @var Config
     */
    private $config;

    /**
     * @var string
     */
    private $rootDir;

    /**
     * @var Client
     */
    private $client;

    /**
     * @var Collection
     */
    private $logCollection;

    /**
     * @var array
     */
    private $appliedRevisions = array();

    /**
     * @var EventDispatcher
     */
    private $eventDispatcher;

    public function __construct(Config $config, $rootDir)
    {
        $this->config = $config;
        $this->rootDir = $rootDir;
        $this->eventDispatcher = new EventDispatcher;
    }

    /**
     * @param string $environment
     *
     * @return Client
     */
    private function getClient($environment)
    {
        if (empty($this->client[$environment])) {
            $this->client[$environment] = new Client(
                $this->config->getDsn($environment),
                $this->config->getConnectOptions($environment)
            );

            $this->client[$environment]->useDatabase($this->config->getDefaultDatabaseName($environment));
        }

        return $this->client[$environment];
    }

    /**
     * @return string
     */
    public function getMigrationsDir()
    {
        $migrationsDir = $this->config->getMigrationsDir();
        if ($migrationsDir[0] === '/') {
            return $migrationsDir;
        }

        return $this->rootDir . '/' . rtrim($migrationsDir, '/');
    }

    /**
     * @param int|null $limit If specified, get only last revisions
     *
     * @return Revision[]
     */
    public function getAvailableRevisions($limit = null)
    {
        if ($limit !==null && !is_integer($limit)) {
            throw new \InvalidArgumentException('Limit must be integer');
        }

        $list = [];

        foreach (new \DirectoryIterator($this->getMigrationsDir()) as $file) {
            if (!$file->isFile()) {
                continue;
            }

            list($id, $className) = explode('_', $file->getBasename('.php'));

            $revision = new Revision();
            $revision
                ->setId($id)
                ->setName($className)
                ->setFilename($file->getFilename());

            $list[$id] = $revision;

            ksort($list);
        }

        if ($limit !== null) {
            $list = array_slice($list, -$limit);
        }

        return $list;
    }

    /**
     * @param string $environment
     *
     * @return Collection
     *
     * @throws \Sokil\Mongo\Exception
     */
    protected function getLogCollection($environment)
    {
        if ($this->logCollection) {
            return $this->logCollection;
        }

        $databaseName = $this->config->getLogDatabaseName($environment);
        $collectionName = $this->config->getLogCollectionName($environment);

        $this->logCollection = $this
            ->getClient($environment)
            ->getDatabase($databaseName)
            ->getCollection($collectionName);

        return $this->logCollection;
    }

    /**
     * @param string $revision
     * @param string $environment
     *
     * @return self
     *
     * @throws \Sokil\Mongo\Exception
     * @throws \Sokil\Mongo\Exception\WriteException
     */
    protected function logUp($revision, $environment)
    {
        $this
            ->getLogCollection($environment)
            ->createDocument(array(
                'revision'  => $revision,
                'date'      => new \MongoDate,
            ))
            ->save();

        return $this;
    }

    /**
     * @param string $revision
     * @param string $environment
     *
     * @return self
     *
     * @throws \Sokil\Mongo\Exception
     */
    protected function logDown($revision, $environment)
    {
        $collection = $this->getLogCollection($environment);
        $collection->batchDelete($collection->expression()->where('revision', $revision));

        return $this;
    }

    /**
     * @param string $environment
     *
     * @return array
     *
     * @throws \Sokil\Mongo\Exception
     */
    public function getAppliedRevisions($environment)
    {
        if (isset($this->appliedRevisions[$environment])) {
            return $this->appliedRevisions[$environment];
        }

        $documents = array_values(
            $this
                ->getLogCollection($environment)
                ->find()
                ->sort(array('revision' => 1))
                ->map(function ($document) {
                    return $document->revision;
                })
        );

        if (!$documents) {
            return array();
        }

        $this->appliedRevisions[$environment] = $documents;

        return $this->appliedRevisions[$environment];
    }

    /**
     * @param string $revision
     * @param string $environment
     *
     * @return bool
     *
     * @throws \Sokil\Mongo\Exception
     */
    public function isRevisionApplied($revision, $environment)
    {
        return in_array($revision, $this->getAppliedRevisions($environment));
    }

    /**
     * @param string $environment
     *
     * @return string
     *
     * @throws \Sokil\Mongo\Exception
     */
    protected function getLatestAppliedRevisionId($environment)
    {
        $revisions = $this->getAppliedRevisions($environment);
        return end($revisions);
    }

    /**
     * @param string $targetRevision
     * @param string $environment
     * @param string $direction
     *
     * @throws \Sokil\Mongo\Exception
     * @throws \Sokil\Mongo\Exception\WriteException
     */
    protected function executeMigration($targetRevision, $environment, $direction)
    {
        $this->eventDispatcher->dispatch('start');

        // get last applied migration
        $latestRevisionId = $this->getLatestAppliedRevisionId($environment);

        // get list of migrations
        $availableRevisions = $this->getAvailableRevisions();

        // execute
        if ($direction === 1) {
            $this->eventDispatcher->dispatch('before_migrate');

            ksort($availableRevisions);

            foreach ($availableRevisions as $revision) {
                if ($revision->getId() <= $latestRevisionId) {
                    continue;
                }

                $event = new ApplyRevisionEvent();
                $event->setRevision($revision);

                $this->eventDispatcher->dispatch('before_migrate_revision', $event);

                $revisionPath = $this->getMigrationsDir() . '/' . $revision->getFilename();
                require_once $revisionPath;

                $className = $revision->getName();

                $migration = new $className(
                    $this->getClient($environment)
                );

                $migration->setEnvironment($environment);

                $migration->up();

                $this->logUp($revision->getId(), $environment);

                $this->eventDispatcher->dispatch('migrate_revision', $event);

                if ($targetRevision && in_array($targetRevision, array($revision->getId(), $revision->getName()))) {
                    break;
                }
            }

            $this->eventDispatcher->dispatch('migrate');
        } else {
            $this->eventDispatcher->dispatch('before_rollback');

            // check if nothing to revert
            if (!$latestRevisionId) {
                return;
            }

            krsort($availableRevisions);

            foreach ($availableRevisions as $revision) {
                if ($revision->getId() > $latestRevisionId) {
                    continue;
                }

                if ($targetRevision && in_array($targetRevision, array($revision->getId(), $revision->getName()))) {
                    break;
                }

                $event = new ApplyRevisionEvent();
                $event->setRevision($revision);

                $this->eventDispatcher->dispatch('before_rollback_revision', $event);

                $revisionPath = $this->getMigrationsDir() . '/' . $revision->getFilename();
                require_once $revisionPath;

                $className = $revision->getName();

                $migration = new $className($this->getClient($environment));
                $migration->setEnvironment($environment);
                $migration->down();

                $this->logDown($revision->getId(), $environment);

                $this->eventDispatcher->dispatch('rollback_revision', $event);

                if (!$targetRevision) {
                    break;
                }
            }

            $this->eventDispatcher->dispatch('rollback');
        }

        $this->eventDispatcher->dispatch('stop');

        // clear cached applied revisions
        unset($this->appliedRevisions[$environment]);
    }

    /**
     * @param string $revision
     * @param string $environment
     *
     * @return self
     *
     * @throws \Sokil\Mongo\Exception
     * @throws \Sokil\Mongo\Exception\WriteException
     */
    public function migrate($revision, $environment)
    {
        $this->executeMigration($revision, $environment, 1);

        return $this;
    }

    /**
     * @param string $revision
     * @param string $environment
     *
     * @return self
     *
     * @throws \Sokil\Mongo\Exception
     * @throws \Sokil\Mongo\Exception\WriteException
     */
    public function rollback($revision, $environment)
    {
        $this->executeMigration($revision, $environment, -1);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onStart($listener)
    {
        $this->eventDispatcher->addListener('start', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onBeforeMigrate($listener)
    {
        $this->eventDispatcher->addListener('before_migrate', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onBeforeMigrateRevision($listener)
    {
        $this->eventDispatcher->addListener('before_migrate_revision', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onMigrateRevision($listener)
    {
        $this->eventDispatcher->addListener('migrate_revision', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onMigrate($listener)
    {
        $this->eventDispatcher->addListener('migrate', $listener);

        return $this;
    }

    public function onBeforeRollback($listener)
    {
        $this->eventDispatcher->addListener('before_rollback', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onBeforeRollbackRevision($listener)
    {
        $this->eventDispatcher->addListener('before_rollback_revision', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onRollbackRevision($listener)
    {
        $this->eventDispatcher->addListener('rollback_revision', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onRollback($listener)
    {
        $this->eventDispatcher->addListener('rollback', $listener);

        return $this;
    }

    /**
     * @param callable $listener
     *
     * @return self
     */
    public function onStop($listener)
    {
        $this->eventDispatcher->addListener('stop', $listener);

        return $this;
    }
}


<?php

namespace Sokil\Mongo\Migrator;

/**
 * Migration config
 */
class Config
{
    const ENV_PARAMETER_PATTERN = '/%env\(([a-z-A-Z0-9_]+)\)%/';

    /**
     * @var array
     */
    private $config;

    /**
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->config = $config;
    }

    /**
     * @param $name
     *
     * @return mixed|null
     */
    public function __get($name)
    {
        return isset($this->config[$name]) ? $this->config[$name] : null;
    }

    /**
     * @param string $name
     *
     * @return array|string|int|null
     *
     * @throws \RuntimeException when param defined as env variable but env variable not defined
     */
    public function get($name)
    {
        $value = null;

        // get value
        if (false === strpos($name, '.')) {
            $value = isset($this->config[$name]) ? $this->config[$name] : null;
        } else {
            $value = $this->config;
            foreach (explode('.', $name) as $field) {
                if (!isset($value[$field])) {
                    return null;
                }

                $value = $value[$field];
            }
        }

        // replace value with env variable
        $value = $this->replaceConfigValuePlaceholders($value);

        return $value;
    }

    /**
     * Convert placeholders in values of any config section
     *
     * @param array|string $value
     *
     * @return array|string
     */
    private function replaceConfigValuePlaceholders($value)
    {
        if (is_array($value)) {
            foreach ($value as $subKey => $subValue) {
                $value[$subKey] = $this->replaceConfigValuePlaceholders($subValue);
            }
        } else {
            // replace environment variable
            if (preg_match(self::ENV_PARAMETER_PATTERN, $value, $matches)) {
                $envValue = getenv($matches[1]);
                if ($envValue === false) {
                    throw new \RuntimeException(sprintf(
                        'No environment variable found with name %s',
                        $matches[1]
                    ));
                }

                $value = $envValue;
            }
        }

        return $value;
    }

    /**
     * @return string
     */
    public function getMigrationsDir()
    {
        return rtrim($this->get('path.migrations'), '/');
    }

    /**
     * @return string|null
     */
    public function getDefaultEnvironment()
    {
        return $this->get('default_environment');
    }

    /**
     * @param string|null $environment
     *
     * @return string|null
     */
    public function getDefaultDatabaseName($environment = null)
    {
        if (!$environment) {
            $environment = $this->getDefaultEnvironment();
        }

        return $this->get(sprintf('environments.%s.default_database', $environment));
    }

    /**
     * @param string|null $environment
     *
     * @return string|null
     */
    public function getDsn($environment = null)
    {
        if (!$environment) {
            $environment = $this->getDefaultEnvironment();
        }

        return $this->get(sprintf('environments.%s.dsn', $environment));
    }

    /**
     * @param string|null $environment
     *
     * @return array
     */
    public function getConnectOptions($environment = null)
    {
        if (!$environment) {
            $environment = $this->getDefaultEnvironment();
        }

        $connectOptions = $this->get(sprintf('environments.%s.connectOptions', $environment));
        if (empty($connectOptions)) {
            $connectOptions = array();
        }

        return $connectOptions;
    }

    /**
     * @param string|null $environment
     *
     * @return string|null
     */
    public function getLogDatabaseName($environment = null)
    {
        if (!$environment) {
            $environment = $this->getDefaultEnvironment();
        }

        return $this->get(sprintf('environments.%s.log_database', $environment));
    }

    /**
     * @param string|null $environment
     *
     * @return string|null
     */
    public function getLogCollectionName($environment = null)
    {
        if (!$environment) {
            $environment = $this->getDefaultEnvironment();
        }

        return $this->get(sprintf('environments.%s.log_collection', $environment));
    }
}


<?php

namespace Sokil\Mongo\Migrator\Console\Command;

use Sokil\Mongo\Exception;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;

class Status extends \Sokil\Mongo\Migrator\Console\Command
{
    protected function configure()
    {
        $this
            ->setName('status')
            ->setDescription('Show status of migrations')
            ->addOption(
                '--environment',
                '-e',
                InputOption::VALUE_OPTIONAL,
                'Environment name'
            )
            ->addOption(
                '--length',
                '-l',
                InputOption::VALUE_OPTIONAL,
                'Limit list by number of last revisions. If not set, show all revisions.'
            )
            ->setHelp('Show list of migrations with status of applying');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     *
     * @return int|null|void
     *
     * @throws Exception
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // length of list
        $length = $input->getOption('length');
        if (empty($length)) {
            $length = null;
        } elseif (is_numeric($length)) {
            $length = (int)$length;
        } else {
            throw new \InvalidArgumentException('Length must be numeric, if specified');
        }

        // environment
        $environment = $input->getOption('environment');
        if (!$environment) {
            $environment = $this->getConfig()->getDefaultEnvironment();
        }

        $output->writeln('Environment: <comment>' . $environment . '</comment>');

        // header
        $columnWidth = array(16, 8, 16);
        $output->writeln('');
        $output->writeln(' ' .
            str_pad('Revision', $columnWidth[0], ' ') .
            str_pad('Status', $columnWidth[1], ' ') .
            str_pad('Name', $columnWidth[2], ' '));

        $output->writeln(str_repeat('-', 35));

        // body
        $manager = $this->getManager();

        foreach ($manager->getAvailableRevisions($length) as $revision) {
            if ($manager->isRevisionApplied($revision->getId(), $environment)) {
                $status = '<info>up</info>' . str_repeat(' ', $columnWidth[1] - 2);
            } else {
                $status = '<error>down</error>' . str_repeat(' ', $columnWidth[1] - 4);
            }

            $output->writeln(' ' .
                str_pad($revision->getId(), $columnWidth[0], ' ') .
                $status .
                str_pad($revision->getName(), $columnWidth[2], ' '));
        }

        $output->writeln('');
    }
}


<?php
/**
 * Http view resolver. It will try to map the given action name to a full path
 * in the filesystem, accoding to the path, prefix, and suffix configured.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Mvc
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Mvc\Http;
use Ding\Logger\ILoggerAware;
use Ding\Mvc\IViewResolver;
use Ding\Mvc\ModelAndView;

/**
 * Http view resolver. It will try to map the given action name to a full path
 * in the filesystem, accoding to the path, prefix, and suffix configured.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Mvc
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 */
class HttpViewResolver implements IViewResolver, ILoggerAware
{
    /**
     * log4php logger or our own.
     * @var Logger
     */
    private $_logger;

    /**
     * Path where views are stored.
     * @var string
     */
    private $_path;

    /**
     * Views prefix.
     * @var string
     */
    private $_prefix;

    /**
     * Views suffix.
     * @var string
     */
    private $_suffix;

    /**
     * This will resolve the given ModelAndView to a view in the filesystem
     * (an absolute path to a file).
     *
     * @param ModelAndView $modelAndView What to render.
     *
     * @see Ding\Mvc.IViewResolver::resolve()
     * @return HttpView
     */
    public function resolve(ModelAndView $modelAndView)
    {
        $name = $modelAndView->getName();
        $path = $this->_path .
            DIRECTORY_SEPARATOR .
            $this->_prefix . $name . $this->_suffix
        ;
        if ($this->_logger->isDebugEnabled()) {
            $this->_logger->debug('Using viewpath: ' . $path);
        }
        return new HttpView($modelAndView, $path);
    }

    /**
     * Sets the path where views are located.
     *
     * @param string $path Path to the views directory.
     *
     * @return void
     */
    public function setViewPath($path)
    {
        $len = strlen($path) - 1;
        if ($path[$len] == '/') {
            $path = substr($path, 0, -1);
        }
        $realpath = realpath($path);;
        $this->_path = $realpath === false ? $path : $realpath;
    }

    /**
     * Sets the view prefix, like view.
     *
     * @param string $prefix Prefix to use for views.
     *
     * @return void
     */
    public function setViewPrefix($prefix)
    {
        $this->_prefix = $prefix;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Logger.ILoggerAware::setLogger()
     */
    public function setLogger(\Logger $logger)
    {
        $this->_logger = $logger;
    }

    /**
     * Sets the view suffix, like .html
     *
     * @param string $suffix Suffix to use for views.
     *
     * @return void
     */
    public function setViewSuffix($suffix)
    {
        $this->_suffix = $suffix;
    }
}


<?php

namespace diversen;

/**
 * Sends a file to a client, with support for (multiple) range requests.
 * It is also able to throttle the download.",
 */
class sendfile
{
    //public
    /**
     * if false we set content disposition from file that will be sent
     * @var mixed $disposition
     */
    private $disposition = false;

    /**
     * throttle speed in secounds
     * @var float $sec
     */
    private $sec = 0.1;

    /**
     * bytes per $sec
     * @var int $bytes
     */
    private $bytes = 40960;

    /**
     * if contentType is false we try to guess it
     * @var mixed $contentType
     */
    private $type = false;

    /**
     * set content disposition
     * @param type $file_name
     */
    public function contentDisposition ($file_name = false) {
        $this->disposition = $file_name;
    }

    /**
     * set throttle speed
     * @param float $sec
     * @param int $bytes
     */
    public function throttle ($sec = 0.1, $bytes = 40960) {
        $this->sec = $sec;
        $this->bytes = $bytes;
    }

    /**
     * set content mime type if false we try to guess it
     * @param string $content_type
     */
    public function contentType ($content_type = null) {
        $this->type = $content_type;
    }

    /**
     * get name from path info
     * @param type $file
     * @return type
     */
    private function name ($file) {
        $info = pathinfo($file);
        return $info['basename'];
    }

    /**
     * Sets-up headers and starts transfering bytes
     *
     * @param string  $file_path
     * @param boolean $withDisposition
     * @throws Exception
     */
    public function send($file_path, $withDisposition=TRUE) {

        if (!is_readable($file_path)) {
            throw new \Exception('File not found or inaccessible!');
        }

        $size = filesize($file_path);
        if (!$this->disposition) {
            $this->disposition = $this->name($file_path);
        }

        if (!$this->type) {
            $this->type = $this->getContentType($file_path);
        }

        //turn off output buffering to decrease cpu usage
        $this->cleanAll();

        // required for IE, otherwise Content-Disposition may be ignored
        if (ini_get('zlib.output_compression')) {
            ini_set('zlib.output_compression', 'Off');
        }

        header('Content-Type: ' . $this->type);
        if ($withDisposition) {
            header('Content-Disposition: attachment; filename="' . $this->disposition . '"');
        }
        header('Accept-Ranges: bytes');

        // The three lines below basically make the
        // download non-cacheable
        header("Cache-control: private");
        header('Pragma: private');
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

        // multipart-download and download resuming support
        if (isset($_SERVER['HTTP_RANGE'])) {
            list($a, $range) = explode("=", $_SERVER['HTTP_RANGE'], 2);
            list($range) = explode(",", $range, 2);
            list($range, $range_end) = explode("-", $range);
            $range = intval($range);
            if (!$range_end) {
                $range_end = $size - 1;
            } else {
                $range_end = intval($range_end);
            }

            $new_length = $range_end - $range + 1;
            header("HTTP/1.1 206 Partial Content");
            header("Content-Length: $new_length");
            header("Content-Range: bytes $range-$range_end/$size");
        } else {
            $new_length = $size;
            header("Content-Length: " . $size);
        }

        /* output the file itself */
        $chunksize = $this->bytes; //you may want to change this
        $bytes_send = 0;

        $file = @fopen($file_path, 'rb');
        if ($file) {
            if (isset($_SERVER['HTTP_RANGE'])) {
                fseek($file, $range);
            }

            while (!feof($file) && (!connection_aborted()) && ($bytes_send < $new_length) ) {
                $buffer = fread($file, $chunksize);
                echo($buffer); //echo($buffer); // is also possible
                flush();
                usleep($this->sec * 1000000);
                $bytes_send += strlen($buffer);
            }
            fclose($file);
        } else {
            throw new \Exception('Error - can not open file.');
        }
    }

    /**
     * method for getting mime type of a file
     * @param string $path
     * @return string $mime_type
     */
    private function getContentType($path) {
        $result = false;
        if (is_file($path) === true) {
            if (function_exists('finfo_open') === true) {
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                if (is_resource($finfo) === true) {
                    $result = finfo_file($finfo, $path);
                }
                finfo_close($finfo);
            } else if (function_exists('mime_content_type') === true) {
                $result = preg_replace('~^(.+);.*$~', '$1', mime_content_type($path));
            } else if (function_exists('exif_imagetype') === true) {
                $result = image_type_to_mime_type(exif_imagetype($path));
            }
        }
        return $result;
    }

    /**
     * clean all buffers
     */
    private function cleanAll() {
        while (ob_get_level()) {
            ob_end_clean();
        }
    }
}


<?php
/**
 * Default implementation for a MessageSource. Will try to find a file called
 * bundle_locale.properties and resolve message id's as standard key values in
 * a properties file.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    MessageSource
 * @subpackage Impl
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\MessageSource\Impl;

use Ding\MessageSource\IMessageSource;
use Ding\Resource\IResourceLoader;
use Ding\Resource\IResourceLoaderAware;

/**
 * Default implementation for a MessageSource. Will try to find a file called
 * bundle_locale.properties and resolve message id's as standard key values in
 * a properties file.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    MessageSource
 * @subpackage Impl
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
class MessageSourceImpl implements IMessageSource, IResourceLoaderAware
{
    /**
     * Indexed by bundle name, locale, message id.
     * @var string[][][]
     */
    private $_basenames;

    /**
     * Resource loader so we can access the bundle files.
     * @var IResourceLoader
     */
    private $_resourceLoader;

    /**
     * (non-PHPdoc)
     * @see Ding\MessageSource.IMessageSource::getMessage()
     */
    public function getMessage($bundle, $message, array $arguments, $locale = 'default')
    {
        if (!isset($this->_basenames[$bundle][$locale])) {
            $resource = $this->_resourceLoader->getResource(
                'includepath://' .
                implode('_', array($bundle, $locale)) . '.properties'
            );
            $contents = stream_get_contents($resource->getStream());
            $contents = parse_ini_string($contents);
            $this->_basenames[$bundle][$locale] = $contents;
        }
        if (isset($this->_basenames[$bundle][$locale][$message])) {
            $message = $this->_basenames[$bundle][$locale][$message];
            $i = 1;
            foreach ($arguments as $argument) {
                $message = str_replace('{' . $i . '}', $argument, $message);
                $i++;
            }
        } else {
            return false;
        }
        return $message;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResourceLoaderAware::setResourceLoader()
     */
    public function setResourceLoader(IResourceLoader $resourceLoader)
    {
        $this->_resourceLoader = $resourceLoader;
    }

    /**
     * Set bundle names.
     *
     * @param string[] $basenames
     */
    public function setBasenames(array $basenames)
    {
        $this->_basenames = $basenames;
    }

    /**
     * Constructor.
     *
     * @return void
     */
    public function __construct()
    {
        $this->_basenames = array();
        $this->_resourceLoader = false;
    }
}


<?php
/**
 * A filesystem resource (file:// ... ). Can be absolute or relative.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Resource
 * @subpackage Impl
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Resource\Impl;

use Ding\Resource\Exception\ResourceException;
use Ding\Resource\IResource;

/**
 * A filesystem resource (file:// ... ). Can be absolute or relative.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Resource
 * @subpackage Impl
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
class FilesystemResource implements IResource
{
    /**
     * Holds true filename.
     * @var string
     */
    protected $filename;

    /**
     * Holds file resource.
     * @var stream
     */
    protected $fd;

    /**
     * Holds context, created with stream_context_create()
     * @var resource
     */
    protected $context;

    /**
     * This scheme identifies this resource.
     * @var string
     */
    const SCHEME = 'file://';

    /**
     * Length for self::SCHEME
     * @var integer
     */
    const SCHEMELEN = 7;

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResource::exists()
     */
    public function exists()
    {
        return $this->filename !== false && file_exists($this->filename);
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResource::isOpen()
     */
    public function isOpen()
    {
        return $this->fd !== false;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResource::getURL()
     */
    public function getURL()
    {
        return self::SCHEME . $this->filename;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResource::getStream()
     */
    public function getStream()
    {
        if ($this->fd === false) {
            $this->fd
                = $this->context === false
                ? @fopen($this->getURL(), 'r', false)
                : @fopen($this->getURL(), 'r', false, $this->context);
            if ($this->fd === false) {
                throw new ResourceException('Could not open: ' . $this->filename);
            }
        }
        return $this->fd;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResource::createRelative()
     */
    public function createRelative($relativePath)
    {
        return new FilesystemResource(
            self::SCHEME . $this->getFilename() . DIRECTORY_SEPARATOR . $relativePath
        );
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResource::getFilename()
     */
    public function getFilename()
    {
        return $this->filename;
    }

    /**
     * Constructor.
     *
     * @param string   $filename Filename with or without file://.
     * @param resource $context  Context created with stream_context_create().
     *
     * @throws ResourceException
     * @return void
     */
    public function __construct($filename, $context = false)
    {
        $filename = str_replace(self::SCHEME, '', $filename);
        $realpath = realpath($filename);
        $this->filename = $realpath ? $realpath : $filename;
        $this->fd = false;
        $this->context = $context;
    }
}


<?php
/**
 * Example using ding. See also beans.xml.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Examples
 * @subpackage ConfigurationAnnotation
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
ini_set(
    'include_path',
    implode(
        PATH_SEPARATOR,
        array(
            implode(DIRECTORY_SEPARATOR, array('..', '..', '..', 'src', 'mg')),
            ini_get('include_path'),
        )
    )
);

/**
 * @Configuration
 */
class SomeOtherProviderClass
{
    /**
     * @Bean(class=Dependency)
     * @Scope(value=singleton)
     */
    public function dependency()
    {
        return new Dependency();
    }
}
/**
 * @Configuration
 */
class SomeBeanProviderClass
{
    /**
     * @Resource
     */
    protected $dependency;

    /**
     * Notice how the name= here replaces the bean name that comes from the method.
     * This bean should be called someBean (if no name= arguments where passed to the
     * bean annotation.)
     *
     * @Bean(name=someBeanRenamed,class=MyBean)
     * @Scope(value=prototype)
     * @InitMethod(method=aMethod)
     * @DestroyMethod(method=bMethod)
     * @Value(value="arg1")
     * @Value(value="arg2")
     */
    public function someBean($arg1, $arg2)
    {
        $ret = new MyBean($this->dependency, $arg1, $arg2);
        $ret->setSomeProperty('hello world');
        return $ret;
    }
}

class Dependency
{

}

/**
 * This is our bean.
 * @author SomeAuthor
 */
class MyBean
{
    private $_someProperty;
    private $_dependency;
    private $_arg1;
    private $_arg2;

    public function aMethod()
    {
        echo "Init\n";
    }

    public function bMethod()
    {
        echo "Destroy\n";
    }

    public function setSomeProperty($value)
    {
        $this->_someProperty = $value;
    }

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

    public function getDependency()
    {
        return $this->_dependency;
    }
    public function __construct($dependency, $arg1, $arg2)
    {
        $this->_dependency = $dependency;
        $this->_arg1 = $arg1;
        $this->_arg2 = $arg2;
    }
}
require_once 'Ding/Autoloader/Autoloader.php'; // Include ding autoloader.
\Ding\Autoloader\Autoloader::register(); // Call autoloader register for ding autoloader.
use Ding\Container\Impl\ContainerImpl;
use Ding\Reflection\ReflectionFactory;

// Here you configure the container, its subcomponents, drivers, etc.
$properties = array(
    'ding' => array(
        'log4php.properties' => __DIR__ . '/../log4php.properties',
        'factory' => array(
            'bdef' => array( // Both of these drivers are optional. They are both included just for the thrill of it.
                'annotation' => array('scanDir' => array(realpath(__DIR__)))
            ),
        ),
    )
);
$container = ContainerImpl::getInstance($properties);
$bean = $container->getBean('someBeanRenamed');
var_dump($bean);
var_dump($bean->getSomeProperty());
var_dump($bean->getDependency());


<?php
/**
 * This driver will apply all filters to property values.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Resource\IResourceLoader;
use Ding\Resource\IResourceLoaderAware;
use Ding\Bean\BeanConstructorArgumentDefinition;
use Ding\Bean\IBeanDefinitionProvider;
use Ding\Container\IContainerAware;
use Ding\Bean\Lifecycle\IAfterDefinitionListener;
use Ding\Bean\Lifecycle\IAfterConfigListener;
use Ding\Bean\Factory\Filter\ResourceFilter;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Bean\BeanDefinition;
use Ding\Container\IContainer;
use Ding\Bean\Factory\Exception\BeanFactoryException;

/**
 * This driver will apply all filters to property values.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class PropertiesDriver
    implements IContainerAware,
    IBeanDefinitionProvider, IResourceLoaderAware, IAfterConfigListener
{
    /**
     * Setup flag.
     * @var boolean
     */
    private $_setup = false;

    /**
     * Properties.
     * @var string[]
     */
    private $_properties = array();

    /**
     * Already resolved property names
     * @var string[]
     */
    private $_propertiesNames = array();
    /**
     * Container.
     * @var IContainer
     */
    private $_container;

    /**
     * Injected resource loader.
     * @var IResourceLoader
     */
    private $_resourceLoader;

    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->_container = $container;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Resource.IResourceLoaderAware::setResourceLoader()
     */
    public function setResourceLoader(IResourceLoader $resourceLoader)
    {
        $this->_resourceLoader = $resourceLoader;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.IAfterConfigListener::afterConfig()
     */
    public function afterConfig()
    {
        $holder = $this->_container->getBean('PropertiesHolder');
        foreach ($holder->getLocations() as $location) {
            if (is_string($location)) {
                $resource = $this->_resourceLoader->getResource(trim($location));
            } else {
                $resource = $location;
            }
            $contents = stream_get_contents($resource->getStream());
            $this->_container->registerProperties(parse_ini_string($contents, false));
        }
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeanDefinition()
     */
    public function getBeanDefinition($name)
    {
        if ($name == 'PropertiesHolder') {
            $bDef = new BeanDefinition('PropertiesHolder');
            $bDef->setClass('Ding\\Helpers\\Properties\\PropertiesHelper');
            return $bDef;
        }
        return null;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeansListeningOn()
     */
    public function getBeansListeningOn($eventName)
    {
        return array();
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeanDefinitionByClass()
     */
    public function getBeansByClass($class)
    {
        return array();
    }
}


<?php
/**
 * A mapper implementation for http requests.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Mvc
 * @subpackage Http
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Mvc\Http;

use Ding\Mvc\DispatchInfo;
use Ding\Logger\ILoggerAware;
use Ding\Container\IContainer;
use Ding\Container\IContainerAware;
use Ding\Container\Impl\ContainerImpl;
use Ding\Mvc\IMapper;
use Ding\Mvc\Action;

/**
 * A mapper implementation for http requests.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Mvc
 * @subpackage Http
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class HttpUrlMapper implements IMapper, IContainerAware, ILoggerAware
{
    /**
     * log4php logger or our own.
     * @var Logger
     */
    private $_logger;

    /**
     * @var Controller[]
     */
    private $_map;

    /**
     * Used from the Mvc driver to setup annotated controllers.
     * @var string[]
     */
    private static $_annotatedControllers = array();

    /**
     * Used from the Mvc driver to add controllers found by annotations.
     *
     * @param string $url        Url mapped.
     * @param string $controller Name for the bean (autogenerated).
     *
     * @return void
     */
    public static function addAnnotatedController($url, $controller)
    {
        self::$_annotatedControllers[] = array($url, $controller);
    }

    /**
     * Sets the map for this mapper.
     *
     * @param array[] $map An array containing arrays defined like this:
     * [0] => IAction, [1] => IController
     *
     * (non-PHPdoc)
     * @see Ding\Mvc.IMapper::setMap()
     *
     * @return void
     */
    public function setMap(array $map)
    {
        $this->_map = $map;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Logger.ILoggerAware::setLogger()
     */
    public function setLogger(\Logger $logger)
    {
        $this->_logger = $logger;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->container = $container;
    }

    /**
     * This will map a full url, like /A/B/C to an HttpAction and will try to
     * find a controller that can handle it. This will isolate the baseUrl.
     *
     * @param Action $action Original action (coming from the frontcontroller,
     * the full url).
     *
     * @return DispatchInfo
     */
    public function map(Action $actionObject)
    {
        $url = $actionObject->getId();
        // Add a slash to the beginning is none is found after removing the
        // base url.
        if ($url[0] != '/') {
            $url = '/' . $url;
        }
        // Do not take into account the arguments part of the url.
        $url = explode('?', $url);
        $url = $url[0];

        // Add a trailing slash to the result.
        $len = strlen($url) - 1;
        if ($url[$len] != '/') {
            $url .= '/';
        }

        $this->_logger->debug('Trying to match: ' . $url);
        // Lookup a controller that can handle this url.
        $try = array_merge($this->_map, self::$_annotatedControllers);
        $candidates = array();
        foreach ($try as $map) {
            $controllerUrl = $map[0];
            $controller = $map[1];
            $interceptors = array();
            if (isset($map[2])) {
                $interceptors = $map[2];
            }
            if ($controllerUrl[0] != '/') {
                $controllerUrl = '/' . $controllerUrl;
            }
            $len = strlen($controllerUrl);
            if ($controllerUrl[$len - 1] != '/') {
                $controllerUrl = $controllerUrl . '/';
            }
            $controllerUrlStart = strpos($url, $controllerUrl);
            if (!($controllerUrlStart === 0)) {
                continue;
            }
            $start = $controllerUrlStart + strlen($controllerUrl);
            $action = substr($url, $start);
            if ($action === false) {
                $action = 'Main';
            }
            $action = explode('/', $action);
            $action = $action[0];
            if (!is_object($controller)) {
                $controller = $this->container->getBean($controller);
                $this->_logger->debug(
                	"Found as annotated controller: "
                	. "$controllerUrl in " . get_class($controller)
                );
            }
            if (!isset($candidates[$len])) {
                $candidates[$len] = array();
            }
            $candidates[$len][] = new DispatchInfo(
                $actionObject, $controller, $action . 'Action', $interceptors
            );
        }
        if (empty($candidates)) {
            return false;
        }
        krsort($candidates);
        $controllers = array_shift($candidates);
        return array_shift($controllers);
    }

    /**
     * Constructor.
     *
     * @return void
     */
    public function __construct()
    {
        $this->_map = array();
    }
}


<?php
/**
 * An exception mapper implementation for http requests.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Mvc
 * @subpackage Http
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Mvc\Http;

use Ding\Logger\ILoggerAware;
use Ding\Mvc\IMapper;
use Ding\Mvc\Action;
use Ding\Mvc\DispatchInfo;

/**
 * An exception mapper implementation for http requests.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Mvc
 * @subpackage Http
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class HttpExceptionMapper implements IMapper, ILoggerAware
{
    /**
     * log4php logger or our own.
     * @var Logger
     */
    private $_logger;

    /**
     * @var Controller[]
     */
    private $_map;

    /**
     * Sets the map for this mapper.
     *
     * @param array[] $map An array containing arrays defined like this:
     * [0] => IAction, [1] => IController
     *
     * (non-PHPdoc)
     * @see Ding\Mvc.IMapper::setMap()
     *
     * @return void
     */
    public function setMap(array $map)
    {
        $this->_map = $map;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Logger.ILoggerAware::setLogger()
     */
    public function setLogger(\Logger $logger)
    {
        $this->_logger = $logger;
    }
    /**
     * This will map an exception action to a controller. Will look for
     * an instance of the mapped exception to the thrown exception.
     *
     * @param Action $action Original action (exception).
     *
     * @return array [0] => Controller [1] => Method to call (With
     * 'Exception' appended to the end of the method name).
     */
    public function map(Action $action)
    {
        $exception = $action->getArguments();
        $exception = $exception['exception'];

        $this->_logger->debug('Exception mapper invoked with: ' . $action->getId());

        // Lookup a controller that can handle this url.
        foreach ($this->_map as $map) {
            $controllerException = $map[0];
            $controller = $map[1];
            if (!($exception instanceof $controllerException)) {
                continue;
            }
            return new DispatchInfo(
                $action, $controller,
                str_replace('\\', '_', $controllerException) . 'Exception'
            );
        }
        return false;
    }

    /**
     * Constructor.
     *
     * @return void
     */
    public function __construct()
    {
        $this->_map = array();
        $this->_baseUrl = '/';
    }
}


<?php
/**
 * Aspect Manager.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Aspect
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Aspect;

use Ding\Bean\Lifecycle\IAfterConfigListener;

/**
 * Aspect Manager.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Aspect
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 */
class AspectManager implements IAfterConfigListener
{
    /**
     * Holds known aspects. Indexed by name.
     * @var AspectDefinition[]
     */
    private $_aspects = array();

    /**
     * Holds known pointcuts. Indexed by name.
     * @var PointcutDefinition[]
     */
    private $_pointcuts = array();

    /**
     * Aspect cache to cache aspects and pointcuts.
     * @var ICache
     */
    private $_cache;

    /**
     * Aspect definition providers.
     * @var IAspectProvider[]
     */
    private $_aspectProviders = array();

    /**
     * Pointcut definition providers.
     * @var IPointcutProvider[]
     */
    private $_pointcutProviders = array();

    /**
     * Serialization.
     *
     * @return array
     */
    public function __sleep()
    {
        return array('_aspects', '_pointcuts');
    }

    /**
     * Adds or overwrites the given aspect.
     *
     * @param AspectDefinition $aspect Aspect.
     *
     * @return void
     */
    public function setAspect(AspectDefinition $aspect)
    {
        $name = $aspect->getName();
        $this->_aspects[$name] = $aspect;
        $this->_cache->store('AspectManagerAspect' . $name, $aspect);
        $this->_cache->store('Aspects', $this->_aspects);
    }

    /**
     * Adds or overwrites the given pointcut.
     *
     * @param PointcutDefinition $pointcut Pointcut.
     *
     * @return void
     */
    public function setPointcut(PointcutDefinition $pointcut)
    {
        $name = $pointcut->getName();
        $this->_pointcuts[$name] = $pointcut;
        $this->_cache->store('AspectManagerPointcut' . $name, $pointcut);
    }

    /**
     * Return all known aspects, indexed by name.
     *
     * @return AspectDefinition[]
     */
    public function getAspects()
    {
        return $this->_aspects;
    }

    /**
     * Returns a PointcutDefinition or false if none found.
     *
     * @param string $pointcut Pointcut id or name.
     *
     * @return PointcutDefinition
     */
    public function getPointcut($pointcut)
    {
        if (isset($this->_pointcuts[$pointcut])) {
            return $this->_pointcuts[$pointcut];
        } else {
            $result = false;
            $value = $this->_cache->fetch('AspectManagerPointcut' . $pointcut, $result);
            if ($result === true) {
                $this->_pointcuts[$pointcut] = $value;
                return $value;
            } else {
                foreach ($this->_pointcutProviders as $provider) {
                    $value = $provider->getPointcut($pointcut);
                    if ($value !== false) {
                        $this->setPointcut($value);
                        return $value;
                    }
                }
            }
        }
        return false;
    }

    public function afterConfig()
    {
        $aspects = $this->getAspects();
        if (empty($aspects)) {
            foreach ($this->_aspectProviders as $provider) {
                foreach ($provider->getAspects() as $aspect) {
                    $this->setAspect($aspect);
                }
            }
        }
    }
    /**
     * Will register an aspect definition provider in this manager.
     *
     * @param IAspectProvider $provider Aspect definition provider.
     *
     * @return void
     */
    public function registerAspectProvider(IAspectProvider $provider)
    {
        $this->_aspectProviders[] = $provider;
    }

    /**
     * Will register a pointcut definition provider in this manager.
     *
     * @param IPointcutProvider $provider Pointcut definition provider.
     *
     * @return void
     */
    public function registerPointcutProvider(IPointcutProvider $provider)
    {
        $this->_pointcutProviders[] = $provider;
    }

    /**
     * Sets aspects cache.
     *
     * @param Ding\Cache\ICache $cache
     *
     * @return void
     */
    public function setCache(\Ding\Cache\ICache $cache)
    {
        $this->_cache = $cache;
    }

}


<?php
/**
 * This driver will take care of the method injection.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Bean\IBeanDefinitionProvider;
use Ding\Aspect\IAspectManagerAware;
use Ding\Bean\Lifecycle\IAfterDefinitionListener;
use Ding\Container\IContainerAware;
use Ding\Container\IContainer;
use Ding\Aspect\PointcutDefinition;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Aspect\AspectManager;
use Ding\Aspect\MethodInvocation;
use Ding\Aspect\AspectDefinition;
use Ding\Bean\BeanDefinition;

/**
 * An "inner" class. This is the aspect that runs when the method is called.
 * Enter description here ...
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class MethodInjectionAspect implements IContainerAware
{
    /**
     * Container.
     * @var IContainer
     */
    private $_container;

    /**
     * Bean to generate.
     * @var string
     */
    private $_beanName;

    /**
     * Setter injection for bean name.
     *
     * @param string $beanName Bean name.
     *
     * @return void
     */
    public function setBeanName($beanName)
    {
        $this->_beanName = $beanName;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->_container = $container;
    }

    /**
     * Creates a new bean (prototypes).
     *
     * @param MethodInvocation $invocation The call.
     *
     * @return object
     */
    public function invoke(MethodInvocation $invocation)
    {
        return $this->_container->getBean($this->_beanName);
    }
}

/**
 * This driver will take care of the method injection.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class MethodInjectionDriver
    implements IAfterDefinitionListener, IAspectManagerAware,
    IContainerAware, IBeanDefinitionProvider
{
    private $_aspectManager;
    private $_beans = array();
    /**
     * Container.
     * @var IContainer
     */
    private $_container;

    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->_container = $container;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeanDefinition()
     */
    public function getBeanDefinition($name)
    {
        if (isset($this->_beans[$name])) {
            return $this->_beans[$name];
        }
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeanDefinitionByClass()
     */
    public function getBeansByClass($class)
    {
        return array();
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeansListeningOn()
     */
    public function getBeansListeningOn($eventName)
    {
        return array();
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.IAfterDefinitionListener::afterDefinition()
     */
    public function afterDefinition(BeanDefinition $bean)
    {
        foreach ($bean->getMethodInjections() as $method) {
            $aspectBeanName = BeanDefinition::generateName('MethodInjectionAspect');
            $aspectBean = new BeanDefinition($aspectBeanName);
            $aspectBean->setClass('\\Ding\\Bean\\Factory\\Driver\\MethodInjectionAspect');
            $aspectBean->setProperties(array(
                new BeanPropertyDefinition('beanName', BeanPropertyDefinition::PROPERTY_SIMPLE, $method[1])
            ));
            $this->_beans[$aspectBeanName] = $aspectBean;
            $aspectName = BeanDefinition::generateName('MethodInjectionAspect');
            $pointcutName = BeanDefinition::generateName('MethodInjectionPointcut');
            $pointcut = new PointcutDefinition($pointcutName, $method[0], 'invoke');
            $this->_aspectManager->setPointcut($pointcut);
            $aspect = new AspectDefinition(
                $aspectName, array($pointcutName),
                AspectDefinition::ASPECT_METHOD, $aspectBeanName, ''
            );
            $aspects = $bean->getAspects();
            $aspects[] = $aspect;
            $bean->setAspects($aspects);
        }
        return $bean;
    }

    public function setAspectManager(AspectManager $aspectManager)
    {
        $this->_aspectManager = $aspectManager;
    }
}


<?php
/**
 * This driver will search for @InitMethod and @DestroyMethod.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Reflection\IReflectionFactory;
use Ding\Reflection\IReflectionFactoryAware;
use Ding\Bean\Factory\Exception\BeanFactoryException;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Bean\Lifecycle\IAfterDefinitionListener;
use Ding\Bean\BeanDefinition;

/**
 * This driver will search for @InitMethod and @DestroyMethod.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class AnnotationInitDestroyMethodDriver implements IAfterDefinitionListener, IReflectionFactoryAware
{
    /**
     * A ReflectionFactory implementation.
     * @var IReflectionFactory
     */
    protected $reflectionFactory;

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->reflectionFactory = $reflectionFactory;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.IAfterDefinitionListener::afterDefinition()
     */
    public function afterDefinition(BeanDefinition $bean)
    {
        $class = $bean->getClass();
        $annotations = $this->reflectionFactory->getClassAnnotations($class);
        if ($annotations->contains('initmethod')) {
            $annotation = $annotations->getSingleAnnotation('initmethod');
            if ($annotation->hasOption('method')) {
                $bean->setInitMethod($annotation->getOptionSingleValue('method'));
            }
        }
        if ($annotations->contains('destroymethod')) {
            $annotation = $annotations->getSingleAnnotation('destroymethod');
            if ($annotation->hasOption('method')) {
                $bean->setDestroyMethod($annotation->getOptionSingleValue('method'));
            }
        }
        foreach ($this->reflectionFactory->getClass($class)->getMethods() as $method) {
            $methodName = $method->getName();
            $annotations = $this->reflectionFactory->getMethodAnnotations($class, $methodName);
            if ($annotations->contains('postconstruct')) {
                $bean->setInitMethod($methodName);
                break;
            }
            if ($annotations->contains('predestroy')) {
                $bean->setDestroyMethod($methodName);
                break;
            }
        }
        return $bean;
    }
}


<?php
/**
 * This driver will wire by type.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Bean\BeanConstructorArgumentDefinition;

use Ding\Annotation\Collection;
use Ding\Annotation\Annotation;
use Ding\Bean\Factory\Exception\InjectByTypeException;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Container\IContainerAware;
use Ding\Container\IContainer;
use Ding\Bean\BeanDefinition;
use Ding\Bean\Lifecycle\IAfterDefinitionListener;
use Ding\Reflection\IReflectionFactory;
use Ding\Reflection\IReflectionFactoryAware;

/**
 * This driver will wire by type.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class AnnotationInjectDriver
    implements IAfterDefinitionListener, IReflectionFactoryAware, IContainerAware
{
    /**
     * A ReflectionFactory implementation.
     * @var IReflectionFactory
     */
    private $_reflectionFactory;

    /**
     * @var IContainer
     */
    private $_container;

    private function _inject($name, Annotation $annotation, $class = null, Annotation $named = null)
    {
        $ret = false;
        $required = true;
        if ($annotation->hasOption('required')) {
            $required = $annotation->getOptionSinglevalue('required') == 'true';
        }
        if (!$annotation->hasOption('type')) {
            if ($class === null) {
                throw new InjectByTypeException($name, 'Unknown', "Missing type= specification");
            }
        } else {
            $class = $annotation->getOptionSingleValue('type');
        }
        $isArray = strpos(substr($class, -2), "[]") !== false;
        if ($isArray) {
            $class = substr($class, 0, -2);
            $ret = array();
        }
        $candidates = $this->_container->getBeansByClass($class);
        if (empty($candidates)) {
            if ($required) {
                throw new InjectByTypeException($name, $class, "Did not find any candidates for injecting by type");
            } else {
                return array();
            }
        }
        if (!$isArray && count($candidates) > 1) {
            $preferredName = null;
            if ($named !== null) {
                if (!$named->hasOption('name')) {
                    throw new InjectByTypeException($name, 'Unknown', "@Named needs the name= specification");
                }
                $preferredName = $named->getOptionSingleValue('name');
            }
            if ($preferredName !== null) {
                if (in_array($preferredName, $candidates)) {
                    $candidates = array($preferredName);
                } else {
                    throw new InjectByTypeException($name, 'Unknown', "Specified bean name in @Named not found");
                }
            } else {
                $foundPrimary = false;
                $beans = $candidates;
                foreach ($beans as $beanName) {
                    $beanCandidateDef = $this->_container->getBeanDefinition($beanName);
                    if ($beanCandidateDef->isPrimaryCandidate()) {
                        if ($foundPrimary) {
                            throw new InjectByTypeException(
                                $name, $class, "Too many (primary) candidates for injecting by type"
                            );
                        }
                        $foundPrimary = true;
                        $candidates = array($beanName);
                    }
                }
            }
            if (count($candidates) > 1) {
                throw new InjectByTypeException($name, $class, "Too many candidates for injecting by type");
            }
        }

        if ($isArray) {
            $propertyValue = array();
            foreach ($candidates as $value) {
                $ret[] = $value;
            }
        } else {
            $ret = array_shift($candidates);
        }
        return $ret;
    }

    private function _arrayToConstructorArguments($name, $beanNames)
    {
        $ret = array();
        $type = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_BEAN;
        $value = $beanNames;
        if (is_array($beanNames)) {
            $value = array();
            $type = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_ARRAY;
            foreach ($beanNames as $arg) {
                $value[] = new BeanConstructorArgumentDefinition(
                    BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_BEAN, $arg, $name
                );
            }
        }
        $ret[$name] = new BeanConstructorArgumentDefinition($type, $value, $name);
        return $ret;
    }
    private function _arrayToBeanProperties($name, $beanNames)
    {
        $ret = array();
        $propertyName = $name;
        $propertyValue = $beanNames;
        $propertyType = BeanPropertyDefinition::PROPERTY_BEAN;
        if (is_array($beanNames)) {
            $propertyType = BeanPropertyDefinition::PROPERTY_ARRAY;
            $propertyValue = array();
            foreach ($beanNames as $value) {
                $propertyValue[] = new BeanPropertyDefinition(
                    $value, BeanPropertyDefinition::PROPERTY_BEAN, $value
                );
            }
        }
        $ret[$propertyName] = new BeanPropertyDefinition(
            $propertyName, $propertyType, $propertyValue
        );
        return $ret;
    }

    private function _injectProperties(BeanDefinition $bean)
    {
        $class = $bean->getClass();
        $rClass = $this->_reflectionFactory->getClass($class);
        $properties = $bean->getProperties();
        foreach ($rClass->getProperties() as $property) {
            $propertyName = $property->getName();
            $annotations = $this->_reflectionFactory->getPropertyAnnotations($class, $propertyName);
            if (!$annotations->contains('inject')) {
                continue;
            }
            $namedAnnotation = null;
            if ($annotations->contains('named')) {
                $namedAnnotation = $annotations->getSingleAnnotation('named');
            }
            $annotation = $annotations->getSingleAnnotation('inject');
            $newProperties = $this->_arrayToBeanProperties(
                $propertyName, $this->_inject($propertyName, $annotation, null, $namedAnnotation)
            );
            $properties = array_merge($properties, $newProperties);
        }
        $bean->setProperties($properties);
    }

    private function _injectMethods(BeanDefinition $bean)
    {
        $class = $bean->getClass();
        $rClass = $this->_reflectionFactory->getClass($class);
        $properties = $bean->getProperties();
        foreach ($rClass->getMethods() as $method) {
            $methodName = $method->getName();
            $annotations = $this->_reflectionFactory->getMethodAnnotations($class, $methodName);
            if (!$annotations->contains('inject')
                || $annotations->contains('bean')
                || $method->isConstructor()
            ) {
                continue;
            }
            $annotation = $annotations->getSingleAnnotation('inject');
            $namedAnnotation = null;
            if ($annotations->contains('named')) {
                $namedAnnotation = $annotations->getSingleAnnotation('named');
            }
            // Just 1 arg now. Multiple arguments need support in the container side.
            $parameters = $method->getParameters();
            if (empty($parameters)) {
                throw new InjectByTypeException($methodName, $methodName, 'Nothing to inject (no arguments in method)');
            }
            if (count($parameters) > 1) {
                throw new InjectByTypeException($methodName, $methodName, 'Multiple arguments are not yet supported');
            }
            $type = array_shift($parameters);
            $type = $type->getClass();
            if ($type !== null) {
                $type = $type->getName();
            }
            $newProperties = $this->_arrayToBeanProperties(
                $methodName, $this->_inject($methodName, $annotation, $type, $namedAnnotation)
            );
            $properties = array_merge($properties, $newProperties);
        }
        $bean->setProperties($properties);
    }

    private function _applyToConstructor(\ReflectionMethod $rMethod, Collection $beanAnnotations, BeanDefinition $bean)
    {
        $constructorArguments = $bean->getArguments();
        if (!$beanAnnotations->contains('inject')) {
            return;
        }
        $annotations = $beanAnnotations->getAnnotations('inject');
        foreach ($annotations as $annotation) {
            if ($annotation->hasOption('type')) {
                if (!$annotation->hasOption('name')) {
                    throw new InjectByTypeException(
                    	'constructor', 'Unknown', 'Cant specify type without name'
                    );
                }
            }
            if ($annotation->hasOption('name')) {
                if (!$annotation->hasOption('type')) {
                    throw new InjectByTypeException(
                    	'constructor', 'Unknown', 'Cant specify name without type'
                    );
                }
                $name = $annotation->getOptionSingleValue('name');
                $type = $annotation->getOptionSingleValue('type');
                $namedAnnotation = null;
                if ($beanAnnotations->contains('named')) {
                    foreach ($beanAnnotations->getAnnotations('named') as $namedAnnotationCandidate) {
                        if ($namedAnnotationCandidate->hasOption('arg')) {
                            $target = $namedAnnotationCandidate->getOptionSingleValue('arg');
                            if ($target == $name) {
                                $namedAnnotation = $namedAnnotationCandidate;
                            }
                        }
                    }
                }
                $newArgs = $this->_inject($name, $annotation, $type, $namedAnnotation);
                $constructorArguments = array_merge(
                    $constructorArguments,
                    $this->_arrayToConstructorArguments($name, $newArgs)
                );
            } else {
                foreach ($rMethod->getParameters() as $parameter) {
                    $parameterName = $parameter->getName();
                    $type = $parameter->getClass();
                    if ($type === null) {
                        continue;
                    }
                    $type = $type->getName();
                    $namedAnnotation = null;
                    if ($beanAnnotations->contains('named')) {
                        foreach ($beanAnnotations->getAnnotations('named') as $namedAnnotationCandidate) {
                            if ($namedAnnotationCandidate->hasOption('arg')) {
                                $target = $namedAnnotationCandidate->getOptionSingleValue('arg');
                                if ($target == $parameterName) {
                                    $namedAnnotation = $namedAnnotationCandidate;
                                }
                            }
                        }
                    }

                    $newArgs = $this->_inject($parameterName, $annotation, $type, $namedAnnotation);
                    $constructorArguments = array_merge(
                        $constructorArguments,
                        $this->_arrayToConstructorArguments($parameterName, $newArgs, $namedAnnotation)
                    );
                }
            }
        }
        $bean->setArguments($constructorArguments);
    }

    private function _injectConstructorArguments(BeanDefinition $bean)
    {
        if ($bean->isCreatedWithFactoryBean()) {
            $factoryMethod = $bean->getFactoryMethod();
            $factoryBean = $bean->getFactoryBean();
            $def = $this->_container->getBeanDefinition($factoryBean);
            $class = $def->getClass();
            $rMethod = $this->_reflectionFactory->getMethod($class, $factoryMethod);
            $annotations = $this->_reflectionFactory->getMethodAnnotations($class, $factoryMethod);
            $this->_applyToConstructor($rMethod, $annotations, $bean);
        } else if ($bean->isCreatedByConstructor()) {
            $class = $bean->getClass();
            $rClass = $this->_reflectionFactory->getClass($class);
            $rMethod = $rClass->getConstructor();
            if ($rMethod) {
                $annotations = $this->_reflectionFactory->getMethodAnnotations(
                    $class, $rMethod->getName()
                );
                $this->_applyToConstructor($rMethod, $annotations, $bean);
            }
        }
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.IAfterDefinitionListener::afterDefinition()
     */
    public function afterDefinition(BeanDefinition $bean)
    {
        $this->_injectProperties($bean);
        $this->_injectMethods($bean);
        $this->_injectConstructorArguments($bean);
        return $bean;
    }

    public function setContainer(IContainer $container)
    {
        $this->_container = $container;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->_reflectionFactory = $reflectionFactory;
    }
}


<?php
/**
 * This driver will search for annotations.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Reflection\IReflectionFactory;
use Ding\Reflection\IReflectionFactoryAware;
use Ding\Cache\ICache;

/**
 * This driver will search for annotations.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class AnnotationDiscovererDriver implements IReflectionFactoryAware
{
    /**
     * A ReflectionFactory implementation.
     * @var IReflectionFactory
     */
    private $_reflectionFactory;

    /**
     * Annotations cache.
     * @var ICache
     */
    private $_cache;

    /**
     * Sets annotations cache.
     *
     * @param Ding\Cache\ICache $cache
     *
     * @return void
     */
    public function setCache(ICache $cache)
    {
        $this->_cache = $cache;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->_reflectionFactory = $reflectionFactory;
    }

    /**
     * Returns all files elegible for scanning for classes.
     *
     * @param string $path Absolute path to a directory or filename.
     *
     * @return string[]
     */
    private function _getCandidateFilesForClassScanning($path)
    {
        $cacheKey = "$path.candidatefiles";
        $result = false;
        $files = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            return $files;
        }
        $files = array();
        if (is_dir($path)) {
            foreach (scandir($path) as $entry) {
                if ($entry == '.' || $entry == '..') {
                    continue;
                }
                $entry = "$path/$entry";
                foreach ($this->_getCandidateFilesForClassScanning($entry) as $file) {
                    $files[] = $file;
                }
            }
        } else if ($this->_isScannable($path)) {
            $files[] = realpath($path);
        }
        $this->_cache->store($cacheKey, $files);
        return $files;
    }

    private function _getClassesFromFile($file)
    {
        $cacheKey = "$file.classesinfile";
        $result = false;
        $classes = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            return $classes;
        }
        $classes = array_merge(get_declared_classes(), get_declared_interfaces());
        require_once $file;
        $newClasses = array_diff(array_merge(get_declared_classes(), get_declared_interfaces()), $classes);
        if (empty($newClasses)) {
            foreach ($classes as $class) {
                $rClass = $this->_reflectionFactory->getClass($class);
                if ($rClass->getFileName() == $file) {
                    $newClasses[] = $rClass->getName();
                }
            }
        }
        $this->_cache->store($cacheKey, $newClasses);
        return $newClasses;
    }

    private function _getClassesFromDirectory($dir)
    {
        $cacheKey = "$dir.classesindir";
        $result = false;
        $classes = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            return $classes;
        }
        $classes = array();
        foreach ($this->_getCandidateFilesForClassScanning($dir) as $file) {
            $classes[$file] = $this->_getClassesFromFile($file);
        }
        $this->_cache->store($cacheKey, $classes);
        return $classes;
    }
    /**
     * Returns true if the given filesystem entry is interesting to scan.
     *
     * @param string $path Filesystem entry.
     */
    private function _isScannable($path)
    {
        $extensionPos = strrpos($path, '.');
        if ($extensionPos === false) {
            return false;
        }
        if (substr($path, $extensionPos, 4) != '.php') {
            return false;
        }
        return true;
    }

    public function parse()
    {
        foreach ($this->_directories as $dir) {
            $classesPerFile = $this->_getClassesFromDirectory($dir);
            foreach ($classesPerFile as $file => $classes) {
                foreach ($classes as $class) {
                    $this->_reflectionFactory->getClassAnnotations($class);
                }
            }
        }
    }

    public function __construct(array $directories)
    {
        $this->_directories = $directories;
    }
}


<?php
/**
 * Simple zend cache implementation (/bridge).
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Cache
 * @subpackage Impl
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Cache\Impl;
use Ding\Cache\ICache;

/**
 * Simple zend cache implementation (/bridge).
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Cache
 * @subpackage Impl
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class ZendCacheImpl implements ICache
{
    /**
     * Holds current instance.
     * @var ZendCacheImpl
     */
    private static $_instance = false;

    /**
     * A Zend_Cache component
     * @Zend_Cache
     */
    private $_zfCache;

    private function _transform($name)
    {
        return
            str_replace('-', '_',
            str_replace('.', '_',
            str_replace('\\', '_',
            str_replace('/', '_',
            $name
        ))));
    }

    /**
     * Returns a cached value.
     *
     * @param string  $name    Key to look for.
     * @param boolean &$result True on success, false otherwise.
     *
     * @return mixed
     */
    public function fetch($name, &$result)
    {
        $result = false;
        $key = $this->_transform($name);
        $value = $this->_zfCache->load($key);
        if ($value != false) {
            $result = true;
            return $value;
        }
        return $result;
    }

    /**
     * Stores a key/value.
     *
     * @param string $name  Key to use.
     * @param mixed  $value Value.
     *
     * @return boolean
     */
    public function store($name, $value)
    {
        return $this->_zfCache->save($value, $this->_transform($name));
    }

    /**
     * Empties the cache.
     *
	 * @return boolean
     */
    public function flush()
    {
        return $this->_zfCache->clean();
    }

    /**
     * Removes a key from the cache.
     *
     * @param string $name Key to remove.
     *
     * @return boolean
     */
    public function remove($name)
    {
        return $this->_zfCache->remove($this->_transform($name));
    }

    /**
     * Returns an instance of a cache.
     *
     * @param array $options Options for the cache backend.
     *
     * @return ZendCacheImpl
     */
    public static function getInstance($options = array())
    {
        if (self::$_instance == false) {
            self::$_instance = new ZendCacheImpl($options);
        }
        return self::$_instance;
    }

    /**
     * Constructor.
     *
	 * @return void
     */
    private function __construct(array $options)
    {
        $this->_zfCache = \Zend_Cache::factory(
            $options['frontend'], $options['backend'],
            $options['frontendoptions'],
            $options['backendoptions']
        );
    }
}


<?php
/**
 * This class carries information from the error helper to your own error
 * handler.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Helpers
 * @subpackage ErrorHandler
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Helpers\ErrorHandler;

/**
 * This class carries information from the error helper to your own error
 * handler.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Helpers
 * @subpackage ErrorHandler
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class ErrorInfo
{
    /**
     * PHP Type of error (E_NOTICE).
     * @return integer
     */
    private $_type;

    /**
     * Error message.
     * @var string
     */
    private $_message;

    /**
     * File that triggered the error.
     * @var string
     */
    private $_file;

    /**
     * Line that triggered the error.
     * @return integer
     */
    private $_line;

    /**
     * Returns PHP Error type.
     *
     * @return integer
     */
    public function getType()
    {
        return $this->_type;
    }

    /**
     * Returns error message.
     *
     * @return string
     */
    public function getMessage()
    {
        return $this->_message;
    }

    /**
     * Returns the file that triggered the error.
     *
     * @return string
     */
    public function getFile()
    {
        return $this->_file;
    }

    /**
     * Returns line that triggered the error.
     *
     * @return integer
     */
    public function getLine()
    {
        return $this->_line;
    }

    /**
     * Returns a human readable string description of the given error type.
     *
     * @param integer $type Error type to convert to string, from getType()
     *
     * @return string
     */
    public static function typeToString($type)
    {
        switch($type)
        {
        case E_USER_ERROR:
            return 'User Error';
        case E_USER_WARNING:
            return 'User Warning';
        case E_USER_NOTICE:
            return 'User Notice';
        case E_USER_DEPRECATED:
            return 'User deprecated';
        case E_DEPRECATED:
            return 'Deprecated';
        case E_RECOVERABLE_ERROR:
            return 'Recoverable error';
        case E_STRICT:
            return 'Strict';
        case E_WARNING:
            return 'Warning';
        case E_NOTICE:
            return 'Notice';
        case E_ERROR:
            return 'Error';
        default:
            return 'Unknown';
        }
    }

    /**
     * Standard.
     *
     * @return string
     */
    public function __toString()
    {
        return
            '[ ErrorInfo: '
            . ' type: ' . self::typeToString($this->getType())
            . ', Message: ' . $this->getMessage()
            . ', File: ' . $this->getFile()
            . ', Line: ' . $this->getLine()
            . ']'
        ;
    }
    /**
     * Constructor.
     *
     * @param integer $type    PHP Error type (E_NOTICE, E_USER_*, etc).
     * @param string  $message Error message.
     * @param string  $file    File that triggered the error.
     * @param integer $line    Line that triggered the error.
     *
     * @return void
     */
    public function __construct($type, $message, $file, $line)
    {
        $this->_type = $type;
        $this->_message = $message;
        $this->_file = $file;
        $this->_line = $line;
    }
}