text:
<?php

/*
 * This file is part of the Symfony package.
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Parser parses YAML strings to convert them to PHP arrays.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Parser
{
    private $offset         = 0;
    private $lines          = array();
    private $currentLineNb  = -1;
    private $currentLine    = '';
    private $refs           = array();

    /**
     * Constructor
     *
     * @param integer $offset The offset of YAML document (used for line numbers in error messages)
     */
    public function __construct($offset = 0)
    {
        $this->offset = $offset;
    }

    /**
     * Parses a YAML string to a PHP value.
     *
     * @param  string $value A YAML string
     *
     * @return mixed  A PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public function parse($value)
    {
        $this->currentLineNb = -1;
        $this->currentLine = '';
        $this->lines = explode("\n", $this->cleanup($value));

        if (function_exists('mb_detect_encoding') && false === mb_detect_encoding($value, 'UTF-8', true)) {
            throw new ParseException('The YAML value does not appear to be valid UTF-8.');
        }

        if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('UTF-8');
        }

        $data = array();
        while ($this->moveToNextLine()) {
            if ($this->isCurrentLineEmpty()) {
                continue;
            }

            // tab?
            if (preg_match('#^\t+#', $this->currentLine)) {
                throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }

            $isRef = $isInPlace = $isProcessed = false;
            if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
                if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] = $matches['value'];
                }

                // array
                if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                    $c = $this->getRealCurrentLineNb() + 1;
                    $parser = new Parser($c);
                    $parser->refs =& $this->refs;
                    $data[] = $parser->parse($this->getNextEmbedBlock());
                } else {
                    if (isset($values['leadspaces'])
                        && ' ' == $values['leadspaces']
                        && preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
                    ) {
                        // this is a compact notation element, add to next block and parse
                        $c = $this->getRealCurrentLineNb();
                        $parser = new Parser($c);
                        $parser->refs =& $this->refs;

                        $block = $values['value'];
                        if (!$this->isNextLineIndented()) {
                            $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2);
                        }

                        $data[] = $parser->parse($block);
                    } else {
                        $data[] = $this->parseValue($values['value']);
                    }
                }
            } else if (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
                try {
                    $key = Inline::parseScalar($values['key']);
                } catch (ParseException $e) {
                    $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                    $e->setSnippet($this->currentLine);

                    throw $e;
                }

                if ('<<' === $key) {
                    if (isset($values['value']) && '*' === substr($values['value'], 0, 1)) {
                        $isInPlace = substr($values['value'], 1);
                        if (!array_key_exists($isInPlace, $this->refs)) {
                            throw new ParseException(sprintf('Reference "%s" does not exist.', $isInPlace), $this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }
                    } else {
                        if (isset($values['value']) && $values['value'] !== '') {
                            $value = $values['value'];
                        } else {
                            $value = $this->getNextEmbedBlock();
                        }
                        $c = $this->getRealCurrentLineNb() + 1;
                        $parser = new Parser($c);
                        $parser->refs =& $this->refs;
                        $parsed = $parser->parse($value);

                        $merged = array();
                        if (!is_array($parsed)) {
                            throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
                        } else if (isset($parsed[0])) {
                            // Numeric array, merge individual elements
                            foreach (array_reverse($parsed) as $parsedItem) {
                                if (!is_array($parsedItem)) {
                                    throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
                                }
                                $merged = array_merge($parsedItem, $merged);
                            }
                        } else {
                            // Associative array, merge
                            $merged = array_merge($merged, $parsed);
                        }

                        $isProcessed = $merged;
                    }
                } else if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] = $matches['value'];
                }

                if ($isProcessed) {
                    // Merge keys
                    $data = $isProcessed;
                // hash
                } else if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
                    // if next line is less indented or equal, then it means that the current value is null
                    if ($this->isNextLineIndented()) {
                        $data[$key] = null;
                    } else {
                        $c = $this->getRealCurrentLineNb() + 1;
                        $parser = new Parser($c);
                        $parser->refs =& $this->refs;
                        $data[$key] = $parser->parse($this->getNextEmbedBlock());
                    }
                } else {
                    if ($isInPlace) {
                        $data = $this->refs[$isInPlace];
                    } else {
                        $data[$key] = $this->parseValue($values['value']);
                    }
                }
            } else {
                // 1-liner followed by newline
                if (2 == count($this->lines) && empty($this->lines[1])) {
                    try {
                        $value = Inline::parse($this->lines[0]);
                    } catch (ParseException $e) {
                        $e->setParsedLine($this->getRealCurrentLineNb() + 1);
                        $e->setSnippet($this->currentLine);

                        throw $e;
                    }

                    if (is_array($value)) {
                        $first = reset($value);
                        if (is_string($first) && '*' === substr($first, 0, 1)) {
                            $data = array();
                            foreach ($value as $alias) {
                                $data[] = $this->refs[substr($alias, 1)];
                            }
                            $value = $data;
                        }
                    }

                    if (isset($mbEncoding)) {
                        mb_internal_encoding($mbEncoding);
                    }

                    return $value;
                }

                switch (preg_last_error()) {
                    case PREG_INTERNAL_ERROR:
                        $error = 'Internal PCRE error.';
                        break;
                    case PREG_BACKTRACK_LIMIT_ERROR:
                        $error = 'pcre.backtrack_limit reached.';
                        break;
                    case PREG_RECURSION_LIMIT_ERROR:
                        $error = 'pcre.recursion_limit reached.';
                        break;
                    case PREG_BAD_UTF8_ERROR:
                        $error = 'Malformed UTF-8 data.';
                        break;
                    case PREG_BAD_UTF8_OFFSET_ERROR:
                        $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
                        break;
                    default:
                        $error = 'Unable to parse.';
                }

                throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }

            if ($isRef) {
                $this->refs[$isRef] = end($data);
            }
        }

        if (isset($mbEncoding)) {
            mb_internal_encoding($mbEncoding);
        }

        return empty($data) ? null : $data;
    }

    /**
     * Returns the current line number (takes the offset into account).
     *
     * @return integer The current line number
     */
    private function getRealCurrentLineNb()
    {
        return $this->currentLineNb + $this->offset;
    }

    /**
     * Returns the current line indentation.
     *
     * @return integer The current line indentation
     */
    private function getCurrentLineIndentation()
    {
        return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' '));
    }

    /**
     * Returns the next embed block of YAML.
     *
     * @param integer $indentation The indent level at which the block is to be read, or null for default
     *
     * @return string A YAML string
     *
     * @throws ParseException When indentation problem are detected
     */
    private function getNextEmbedBlock($indentation = null)
    {
        $this->moveToNextLine();

        if (null === $indentation) {
            $newIndent = $this->getCurrentLineIndentation();

            if (!$this->isCurrentLineEmpty() && 0 == $newIndent) {
                throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        } else {
            $newIndent = $indentation;
        }

        $data = array(substr($this->currentLine, $newIndent));

        while ($this->moveToNextLine()) {
            if ($this->isCurrentLineEmpty()) {
                if ($this->isCurrentLineBlank()) {
                    $data[] = substr($this->currentLine, $newIndent);
                }

                continue;
            }

            $indent = $this->getCurrentLineIndentation();

            if (preg_match('#^(?P<text> *)$#', $this->currentLine, $match)) {
                // empty line
                $data[] = $match['text'];
            } else if ($indent >= $newIndent) {
                $data[] = substr($this->currentLine, $newIndent);
            } else if (0 == $indent) {
                $this->moveToPreviousLine();

                break;
            } else {
                throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        }

        return implode("\n", $data);
    }

    /**
     * Moves the parser to the next line.
     *
     * @return Boolean
     */
    private function moveToNextLine()
    {
        if ($this->currentLineNb >= count($this->lines) - 1) {
            return false;
        }

        $this->currentLine = $this->lines[++$this->currentLineNb];

        return true;
    }

    /**
     * Moves the parser to the previous line.
     */
    private function moveToPreviousLine()
    {
        $this->currentLine = $this->lines[--$this->currentLineNb];
    }

    /**
     * Parses a YAML value.
     *
     * @param  string $value A YAML value
     *
     * @return mixed  A PHP value
     *
     * @throws ParseException When reference does not exist
     */
    private function parseValue($value)
    {
        if ('*' === substr($value, 0, 1)) {
            if (false !== $pos = strpos($value, '#')) {
                $value = substr($value, 1, $pos - 2);
            } else {
                $value = substr($value, 1);
            }

            if (!array_key_exists($value, $this->refs)) {
                throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine);
            }

            return $this->refs[$value];
        }

        if (preg_match('/^(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?$/', $value, $matches)) {
            $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';

            return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers)));
        }

        try {
            return Inline::parse($value);
        } catch (ParseException $e) {
            $e->setParsedLine($this->getRealCurrentLineNb() + 1);
            $e->setSnippet($this->currentLine);

            throw $e;
        }
    }

    /**
     * Parses a folded scalar.
     *
     * @param  string  $separator   The separator that was used to begin this folded scalar (| or >)
     * @param  string  $indicator   The indicator that was used to begin this folded scalar (+ or -)
     * @param  integer $indentation The indentation that was used to begin this folded scalar
     *
     * @return string  The text value
     */
    private function parseFoldedScalar($separator, $indicator = '', $indentation = 0)
    {
        $separator = '|' == $separator ? "\n" : ' ';
        $text = '';

        $notEOF = $this->moveToNextLine();

        while ($notEOF && $this->isCurrentLineBlank()) {
            $text .= "\n";

            $notEOF = $this->moveToNextLine();
        }

        if (!$notEOF) {
            return '';
        }

        if (!preg_match('#^(?P<indent>'.($indentation ? str_repeat(' ', $indentation) : ' +').')(?P<text>.*)$#u', $this->currentLine, $matches)) {
            $this->moveToPreviousLine();

            return '';
        }

        $textIndent = $matches['indent'];
        $previousIndent = 0;

        $text .= $matches['text'].$separator;
        while ($this->currentLineNb + 1 < count($this->lines)) {
            $this->moveToNextLine();

            if (preg_match('#^(?P<indent> {'.strlen($textIndent).',})(?P<text>.+)$#u', $this->currentLine, $matches)) {
                if (' ' == $separator && $previousIndent != $matches['indent']) {
                    $text = substr($text, 0, -1)."\n";
                }
                $previousIndent = $matches['indent'];

                $text .= str_repeat(' ', $diff = strlen($matches['indent']) - strlen($textIndent)).$matches['text'].($diff ? "\n" : $separator);
            } else if (preg_match('#^(?P<text> *)$#', $this->currentLine, $matches)) {
                $text .= preg_replace('#^ {1,'.strlen($textIndent).'}#', '', $matches['text'])."\n";
            } else {
                $this->moveToPreviousLine();

                break;
            }
        }

        if (' ' == $separator) {
            // replace last separator by a newline
            $text = preg_replace('/ (\n*)$/', "\n$1", $text);
        }

        switch ($indicator) {
            case '':
                $text = preg_replace('#\n+$#s', "\n", $text);
                break;
            case '+':
                break;
            case '-':
                $text = preg_replace('#\n+$#s', '', $text);
                break;
        }

        return $text;
    }

    /**
     * Returns true if the next line is indented.
     *
     * @return Boolean Returns true if the next line is indented, false otherwise
     */
    private function isNextLineIndented()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $notEOF = $this->moveToNextLine();

        while ($notEOF && $this->isCurrentLineEmpty()) {
            $notEOF = $this->moveToNextLine();
        }

        if (false === $notEOF) {
            return false;
        }

        $ret = false;
        if ($this->getCurrentLineIndentation() <= $currentIndentation) {
            $ret = true;
        }

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the current line is blank or if it is a comment line.
     *
     * @return Boolean Returns true if the current line is empty or if it is a comment line, false otherwise
     */
    private function isCurrentLineEmpty()
    {
        return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
    }

    /**
     * Returns true if the current line is blank.
     *
     * @return Boolean Returns true if the current line is blank, false otherwise
     */
    private function isCurrentLineBlank()
    {
        return '' == trim($this->currentLine, ' ');
    }

    /**
     * Returns true if the current line is a comment line.
     *
     * @return Boolean Returns true if the current line is a comment line, false otherwise
     */
    private function isCurrentLineComment()
    {
        //checking explicitly the first char of the trim is faster than loops or strpos
        $ltrimmedLine = ltrim($this->currentLine, ' ');

        return $ltrimmedLine[0] === '#';
    }

    /**
     * Cleanups a YAML string to be parsed.
     *
     * @param  string $value The input YAML string
     *
     * @return string A cleaned up YAML string
     */
    private function cleanup($value)
    {
        $value = str_replace(array("\r\n", "\r"), "\n", $value);

        if (!preg_match("#\n$#", $value)) {
            $value .= "\n";
        }

        // strip YAML header
        $count = 0;
        $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#su', '', $value, -1, $count);
        $this->offset += $count;

        // remove leading comments
        $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
        if ($count == 1) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
            $value = $trimmedValue;
        }

        // remove start of the document marker (---)
        $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
        if ($count == 1) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
            $value = $trimmedValue;

            // remove end of the document marker (...)
            $value = preg_replace('#\.\.\.\s*$#s', '', $value);
        }

        return $value;
    }
}


text:
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception interface for all exceptions thrown by the component.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @api
 */
interface ExceptionInterface
{
}

repo_name: michael-romer/zf-boilerplate

repo_language: PHP

mime_type: text/html


text:
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during dumping.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @api
 */
class DumpException extends \RuntimeException implements ExceptionInterface
{
}


text:
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @api
 */
class ParseException extends \RuntimeException implements ExceptionInterface
{
    private $parsedFile;
    private $parsedLine;
    private $snippet;
    private $rawMessage;

    /**
     * Constructor.
     *
     * @param string    $message  The error message
     * @param integer   $lineno   The line where the error occurred
     * @param integer   $snippet  The snippet of code near the problem
     * @param string    $filename The file name where the error occurred
     * @param Exception $previous The previous exception
     */
    public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, Exception $previous = null)
    {
        $this->parsedFile = $parsedFile;
        $this->parsedLine = $parsedLine;
        $this->snippet = $snippet;
        $this->rawMessage = $message;

        $this->updateRepr();

        parent::__construct($this->message, 0, $previous);
    }

    /**
     * Gets the snippet of code near the error.
     *
     * @return string The snippet of code
     */
    public function getSnippet()
    {
        return $this->snippet;
    }

    /**
     * Sets the snippet of code near the error.
     *
     * @param string $parsedFile The filename
     */
    public function setSnippet($snippet)
    {
        $this->snippet = $snippet;

        $this->updateRepr();
    }

    /**
     * Gets the filename where the error occurred.
     *
     * This method returns null if a string is parsed.
     *
     * @return string The filename
     */
    public function getParsedFile()
    {
        return $this->parsedFile;
    }

    /**
     * Sets the filename where the error occurred.
     *
     * @param string $parsedFile The filename
     */
    public function setParsedFile($parsedFile)
    {
        $this->parsedFile = $parsedFile;

        $this->updateRepr();
    }

    /**
     * Gets the line where the error occurred.
     *
     * @return integer The file line
     */
    public function getParsedLine()
    {
        return $this->parsedLine;
    }

    /**
     * Sets the line where the error occurred.
     *
     * @param integer $parsedLine The file line
     */
    public function setParsedLine($parsedLine)
    {
        $this->parsedLine = $parsedLine;

        $this->updateRepr();
    }

    private function updateRepr()
    {
        $this->message = $this->rawMessage;

        $dot = false;
        if ('.' === substr($this->message, -1)) {
            $this->message = substr($this->message, 0, -1);
            $dot = true;
        }

        if (null !== $this->parsedFile) {
            $this->message .= sprintf(' in %s', json_encode($this->parsedFile));
        }

        if ($this->parsedLine >= 0) {
            $this->message .= sprintf(' at line %d', $this->parsedLine);
        }

        if ($this->snippet) {
            $this->message .= sprintf(' (near "%s")', $this->snippet);
        }

        if ($dot) {
            $this->message .= '.';
        }
    }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerDumperInterface is the interface implemented by service container dumper classes.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
interface sfServiceContainerDumperInterface
{
  function dump(array $options = array());
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerInterface is the interface implemented by service container classes.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
interface sfServiceContainerInterface
{
  public function setParameters(array $parameters);

  public function addParameters(array $parameters);

  public function getParameters();

  public function getParameter($name);

  public function setParameter($name, $value);

  public function hasParameter($name);

  public function setService($id, $service);

  public function getService($id);

  public function hasService($name);
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerDumperPhp dumps a service container as a PHP class.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfServiceContainerDumperPhp extends sfServiceContainerDumper
{
  /**
   * Dumps the service container as a PHP class.
   *
   * Available options:
   *
   *  * class:      The class name
   *  * base_class: The base class name
   *
   * @param  array  $options An array of options
   *
   * @return string A PHP class representing of the service container
   */
  public function dump(array $options = array())
  {
    $options = array_merge(array(
      'class'      => 'ProjectServiceContainer',
      'base_class' => 'sfServiceContainer',
    ), $options);

    return
      $this->startClass($options['class'], $options['base_class']).
      $this->addConstructor().
      $this->addServices().
      $this->addDefaultParametersMethod().
      $this->endClass()
    ;
  }

  protected function addServiceInclude($id, $definition)
  {
    if (null !== $definition->getFile())
    {
      return sprintf("    require_once %s;\n\n", $this->dumpValue($definition->getFile()));
    }
  }

  protected function addServiceShared($id, $definition)
  {
    if ($definition->isShared())
    {
      return <<<EOF
    if (isset(\$this->shared['$id'])) return \$this->shared['$id'];

EOF;
    }
  }

  protected function addServiceReturn($id, $definition)
  {
    if ($definition->isShared())
    {
      return <<<EOF

    return \$this->shared['$id'] = \$instance;
  }

EOF;
    }
    else
    {
      return <<<EOF

    return \$instance;
  }

EOF;
    }
  }

  protected function addServiceInstance($id, $definition)
  {
    $class = $this->dumpValue($definition->getClass());

    $arguments = array();
    foreach ($definition->getArguments() as $value)
    {
      $arguments[] = $this->dumpValue($value);
    }

    if (null !== $definition->getConstructor())
    {
      return sprintf("    \$instance = call_user_func(array(%s, '%s')%s);\n", $class, $definition->getConstructor(), $arguments ? ', '.implode(', ', $arguments) : '');
    }
    else
    {
      if ($class != "'".$definition->getClass()."'")
      {
        return sprintf("    \$class = %s;\n    \$instance = new \$class(%s);\n", $class, implode(', ', $arguments));
      }
      else
      {
        return sprintf("    \$instance = new %s(%s);\n", $definition->getClass(), implode(', ', $arguments));
      }
    }
  }

  protected function addServiceMethodCalls($id, $definition)
  {
    $calls = '';
    foreach ($definition->getMethodCalls() as $call)
    {
      $arguments = array();
      foreach ($call[1] as $value)
      {
        $arguments[] = $this->dumpValue($value);
      }

      $calls .= sprintf("    \$instance->%s(%s);\n", $call[0], implode(', ', $arguments));
    }

    return $calls;
  }

  protected function addServiceConfigurator($id, $definition)
  {
    if (!$callable = $definition->getConfigurator())
    {
      return '';
    }

    if (is_array($callable))
    {
      if (is_object($callable[0]) && $callable[0] instanceof sfServiceReference)
      {
        return sprintf("    %s->%s(\$instance);\n", $this->getServiceCall((string) $callable[0]), $callable[1]);
      }
      else
      {
        return sprintf("    call_user_func(array(%s, '%s'), \$instance);\n", $this->dumpValue($callable[0]), $callable[1]);
      }
    }
    else
    {
      return sprintf("    %s(\$instance);\n", $callable);
    }
  }

  protected function addService($id, $definition)
  {
    $name = sfServiceContainer::camelize($id);

    $code = <<<EOF

  protected function get{$name}Service()
  {

EOF;

    $code .=
      $this->addServiceInclude($id, $definition).
      $this->addServiceShared($id, $definition).
      $this->addServiceInstance($id, $definition).
      $this->addServiceMethodCalls($id, $definition).
      $this->addServiceConfigurator($id, $definition).
      $this->addServiceReturn($id, $definition)
    ;

    return $code;
  }

  protected function addServiceAlias($alias, $id)
  {
    $name = sfServiceContainer::camelize($alias);

    return <<<EOF

  protected function get{$name}Service()
  {
    return {$this->getServiceCall($id)};
  }

EOF;
  }

  protected function addServices()
  {
    $code = '';
    foreach ($this->container->getServiceDefinitions() as $id => $definition)
    {
      $code .= $this->addService($id, $definition);
    }

    foreach ($this->container->getAliases() as $alias => $id)
    {
      $code .= $this->addServiceAlias($alias, $id);
    }

    return $code;
  }

  protected function startClass($class, $baseClass)
  {
    return <<<EOF
<?php

class $class extends $baseClass
{
  protected \$shared = array();

EOF;
  }

  protected function addConstructor()
  {
    if (!$this->container->getParameters())
    {
      return '';
    }

    return <<<EOF

  public function __construct()
  {
    parent::__construct(\$this->getDefaultParameters());
  }

EOF;
  }

  protected function addDefaultParametersMethod()
  {
    if (!$this->container->getParameters())
    {
      return '';
    }

    $parameters = $this->exportParameters($this->container->getParameters());

    return <<<EOF

  protected function getDefaultParameters()
  {
    return $parameters;
  }

EOF;
  }

  protected function exportParameters($parameters, $indent = 6)
  {
    $php = array();
    foreach ($parameters as $key => $value)
    {
      if (is_array($value))
      {
        $value = $this->exportParameters($value, $indent + 2);
      }
      elseif ($value instanceof sfServiceReference)
      {
        $value = sprintf("new sfServiceReference('%s')", $value);
      }
      elseif ($value instanceof sfServiceParameter)
      {
        $value = sprintf("\$this->getParameter('%s')", $value);
      }
      else
      {
        $value = var_export($value, true);
      }

      $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
    }

    return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 2));
  }

  protected function endClass()
  {
    return <<<EOF
}

EOF;
  }

  protected function dumpValue($value)
  {
    if (is_array($value))
    {
      $code = array();
      foreach ($value as $k => $v)
      {
        $code[] = sprintf("%s => %s", $this->dumpValue($k), $this->dumpValue($v));
      }

      return sprintf("array(%s)", implode(', ', $code));
    }
    elseif (is_object($value) && $value instanceof sfServiceReference)
    {
      return $this->getServiceCall((string) $value);
    }
    elseif (is_object($value) && $value instanceof sfServiceParameter)
    {
      return sprintf("\$this->getParameter('%s')", strtolower($value));
    }
    elseif (is_string($value))
    {
      if (preg_match('/^%([^%]+)%$/', $value, $match))
      {
        // we do this to deal with non string values (boolean, integer, ...)
        // the preg_replace_callback converts them to strings
        return sprintf("\$this->getParameter('%s')", strtolower($match[1]));
      }
      else
      {
        $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', array($this, 'replaceParameter'), var_export($value, true)));

        // optimize string
        $code = preg_replace(array("/^''\./", "/\.''$/", "/\.''\./"), array('', '', '.'), $code);

        return $code;
      }
    }
    elseif (is_object($value) || is_resource($value))
    {
      throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
    }
    else
    {
      return var_export($value, true);
    }
  }

  public function replaceParameter($match)
  {
    return sprintf("'.\$this->getParameter('%s').'", strtolower($match[2]));
  }

  protected function getServiceCall($id)
  {
    if ('service_container' == $id)
    {
      return '$this';
    }

    return sprintf('$this->getService(\'%s\')', $id);
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerDumperGraphviz dumps a service container as a graphviz file.
 *
 * You can convert the generated dot file with the dot utility (http://www.graphviz.org/):
 *
 *   dot -Tpng container.dot > foo.png
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfServiceContainerDumperGraphviz extends sfServiceContainerDumper
{
  protected $nodes, $edges;

  /**
   * Dumps the service container as a graphviz graph.
   *
   * Available options:
   *
   *  * graph: The default options for the whole graph
   *  * node: The default options for nodes
   *  * edge: The default options for edges
   *  * node.instance: The default options for services that are defined directly by object instances
   *  * node.definition: The default options for services that are defined via service definition instances
   *  * node.missing: The default options for missing services
   *
   * @param  array  $options An array of options
   *
   * @return string The dot representation of the service container
   */
  public function dump(array $options = array())
  {
    $this->options = array(
      'graph' => array('ratio' => 'compress'),
      'node'  => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'),
      'edge'  => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5),
      'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'),
      'node.definition' => array('fillcolor' => '#eeeeee'),
      'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'),
    );

    foreach (array('graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing') as $key)
    {
      if (isset($options[$key]))
      {
        $this->options[$key] = array_merge($this->options[$key], $options[$key]);
      }
    }

    $this->nodes = $this->findNodes();

    $this->edges = array();
    foreach ($this->container->getServiceDefinitions() as $id => $definition)
    {
      $this->edges[$id] = $this->findEdges($id, $definition->getArguments(), true, '');

      foreach ($definition->getMethodCalls() as $call)
      {
        $this->edges[$id] = array_merge(
          $this->edges[$id],
          $this->findEdges($id, $call[1], false, $call[0].'()')
        );
      }
    }

    return $this->startDot().$this->addNodes().$this->addEdges().$this->endDot();
  }

  protected function addNodes()
  {
    $code = '';
    foreach ($this->nodes as $id => $node)
    {
      $aliases = $this->getAliases($id);

      $code .= sprintf("  node_%s [label=\"%s\\n%s\\n\", shape=%s%s];\n", $this->dotize($id), $id.($aliases ? ' ('.implode(', ', $aliases).')' : ''), $node['class'], $this->options['node']['shape'], $this->addAttributes($node['attributes']));
    }

    return $code;
  }

  protected function addEdges()
  {
    $code = '';
    foreach ($this->edges as $id => $edges)
    {
      foreach ($edges as $edge)
      {
        $code .= sprintf("  node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed');
      }
    }

    return $code;
  }

  protected function findEdges($id, $arguments, $required, $name)
  {
    $edges = array();
    foreach ($arguments as $argument)
    {
      if (is_object($argument) && $argument instanceof sfServiceParameter)
      {
        $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null;
      }
      elseif (is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match))
      {
        $argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null;
      }

      if ($argument instanceof sfServiceReference)
      {
        if (!$this->container->hasService((string) $argument))
        {
          $this->nodes[(string) $argument] = array('name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']);
        }

        $edges[] = array('name' => $name, 'required' => $required, 'to' => $argument);
      }
      elseif (is_array($argument))
      {
        $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name));
      }
    }

    return $edges;
  }

  protected function findNodes()
  {
    $nodes = array();

    $container = clone $this->container;

    foreach ($container->getServiceDefinitions() as $id => $definition)
    {
      $nodes[$id] = array('class' => $this->getValue($definition->getClass()), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() ? 'filled' : 'dotted')));

      $container->setServiceDefinition($id, new sfServiceDefinition('stdClass'));
    }

    foreach ($container as $id => $service)
    {
      if (in_array($id, array_keys($container->getAliases())))
      {
        continue;
      }

      if (!$container->hasServiceDefinition($id))
      {
        $nodes[$id] = array('class' => get_class($service), 'attributes' => $this->options['node.instance']);
      }
    }

    return $nodes;
  }

  protected function getValue($value, $default = '')
  {
    return $this->container->resolveValue($value);
  }

  protected function startDot()
  {
    $parameters = var_export($this->container->getParameters(), true);

    return sprintf("digraph sc {\n  %s\n  node [%s];\n  edge [%s];\n\n",
      $this->addOptions($this->options['graph']),
      $this->addOptions($this->options['node']),
      $this->addOptions($this->options['edge'])
    );
  }

  protected function endDot()
  {
    return "}\n";
  }

  protected function addAttributes($attributes)
  {
    $code = array();
    foreach ($attributes as $k => $v)
    {
      $code[] = sprintf('%s="%s"', $k, $v);
    }

    return $code ? ', '.implode(', ', $code) : '';
  }

  protected function addOptions($options)
  {
    $code = array();
    foreach ($options as $k => $v)
    {
      $code[] = sprintf('%s="%s"', $k, $v);
    }

    return implode(' ', $code);
  }

  protected function dotize($id)
  {
    return strtolower(preg_replace('/[^\w]/i', '_', $id));
  }

  protected function getAliases($id)
  {
    $aliases = array();
    foreach ($this->container->getAliases() as $alias => $origin)
    {
      if ($id == $origin)
      {
        $aliases[] = $alias;
      }
    }

    return $aliases;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceReference represents a service reference.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfServiceReference.php 267 2009-03-26 19:56:18Z fabien $
 */
class sfServiceReference
{
  protected
    $id = null;

  /**
   * Constructor.
   *
   * @param string $id The service identifier
   */
  public function __construct($id)
  {
    $this->id = $id;
  }

  /**
   * __toString.
   *
   * @return string The service identifier
   */
  public function __toString()
  {
    return (string) $this->id;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerDumperXml dumps a service container as an XML string.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfServiceContainerDumperXml extends sfServiceContainerDumper
{
  /**
   * Dumps the service container as an XML string.
   *
   * @param  array  $options An array of options
   *
   * @return string An xml string representing of the service container
   */
  public function dump(array $options = array())
  {
    return $this->startXml().$this->addParameters().$this->addServices().$this->endXml();
  }

  protected function addParameters()
  {
    if (!$this->container->getParameters())
    {
      return '';
    }

    return sprintf("  <parameters>\n%s  </parameters>\n", $this->convertParameters($this->escape($this->container->getParameters()), 'parameter', 4));
  }

  protected function addService($id, $definition)
  {
    $code = sprintf("    <service id=\"%s\" class=\"%s\"%s%s>\n",
      $id,
      $definition->getClass(),
      $definition->getConstructor() ? sprintf(' constructor="%s"', $definition->getConstructor()) : '',
      !$definition->isShared() ? ' shared="false"' : ''
    );

    if ($definition->getFile())
    {
      $code .= sprintf("      <file>%s</file>\n", $definition->getFile());
    }

    if ($definition->getArguments())
    {
      $code .= $this->convertParameters($definition->getArguments(), 'argument', 6);
    }

    foreach ($definition->getMethodCalls() as $call)
    {
      if (count($call[1]))
      {
        $code .= sprintf("      <call method=\"%s\">\n%s      </call>\n", $call[0], $this->convertParameters($call[1], 'argument', 8));
      }
      else
      {
        $code .= sprintf("      <call method=\"%s\" />\n", $call[0]);
      }
    }

    if ($callable = $definition->getConfigurator())
    {
      if (is_array($callable))
      {
        if (is_object($callable[0]) && $callable[0] instanceof sfServiceReference)
        {
          $code .= sprintf("      <configurator service=\"%s\" method=\"%s\" />\n", $callable[0], $callable[1]);
        }
        else
        {
          $code .= sprintf("      <configurator class=\"%s\" method=\"%s\" />\n", $callable[0], $callable[1]);
        }
      }
      else
      {
        $code .= sprintf("      <configurator function=\"%s\" />\n", $callable);
      }
    }

    $code .= "    </service>\n";

    return $code;
  }

  protected function addServiceAlias($alias, $id)
  {
    return sprintf("    <service id=\"%s\" alias=\"%s\" />\n", $alias, $id);
  }

  protected function addServices()
  {
    if (!$this->container->getServiceDefinitions())
    {
      return '';
    }

    $code = '';
    foreach ($this->container->getServiceDefinitions() as $id => $definition)
    {
      $code .= $this->addService($id, $definition);
    }

    foreach ($this->container->getAliases() as $alias => $id)
    {
      $code .= $this->addServiceAlias($alias, $id);
    }

    return sprintf("  <services>\n%s  </services>\n", $code);
  }

  protected function convertParameters($parameters, $type='parameter', $depth = 2)
  {
    $white = str_repeat(' ', $depth);
    $xml = '';
    $withKeys = array_keys($parameters) !== range(0, count($parameters) - 1);
    foreach ($parameters as $key => $value)
    {
      $attributes = '';
      $key = $withKeys ? sprintf(' key="%s"', $key) : '';
      if (is_array($value))
      {
        $value = "\n".$this->convertParameters($value, $type, $depth + 2).$white;
        $attributes = ' type="collection"';
      }

      if (is_object($value) && $value instanceof sfServiceReference)
      {
        $xml .= sprintf("%s<%s%s type=\"service\" id=\"%s\" />\n", $white, $type, $key, (string) $value, $type);
      }
      else
      {
        if (in_array($value, array('null', 'true', 'false'), true))
        {
          $attributes = ' type="string"';
        }

        $xml .= sprintf("%s<%s%s%s>%s</%s>\n", $white, $type, $key, $attributes, self::phpToXml($value), $type);
      }
    }

    return $xml;
  }

  protected function startXml()
  {
    return <<<EOF
<?xml version="1.0" ?>

<container xmlns="http://symfony-project.org/2.0/container">

EOF;
  }

  protected function endXml()
  {
    return "</container>\n";
  }

  protected function escape($arguments)
  {
    $args = array();
    foreach ($arguments as $k => $v)
    {
      if (is_array($v))
      {
        $args[$k] = $this->escape($v);
      }
      elseif (is_string($v))
      {
        $args[$k] = str_replace('%', '%%', $v);
      }
      else
      {
        $args[$k] = $v;
      }
    }

    return $args;
  }

  static public function phpToXml($value)
  {
    switch (true)
    {
      case null === $value:
        return 'null';
      case true === $value:
        return 'true';
      case false === $value:
        return 'false';
      case is_object($value) && $value instanceof sfServiceParameter:
        return '%'.$value.'%';
      case is_object($value) || is_resource($value):
        throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
      default:
        return $value;
    }
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainer is a dependency injection container.
 *
 * It gives access to object instances (services), and parameters.
 *
 * Services and parameters are simple key/pair stores.
 *
 * Parameters keys are case insensitive.
 *
 * A service id can contain lowercased letters, digits, underscores, and dots.
 * Underscores are used to separate words, and dots to group services
 * under namespaces:
 *
 *

 *
request

 *
mysql_session_storage

 *
symfony.mysql_session_storage

 *

 *
 * A service can also be defined by creating a method named
 * getXXXService(), where XXX is the camelized version of the id:
 *
 *

 *
request -> getRequestService()

 *
mysql_session_storage -> getMysqlSessionStorageService()

 *
symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()

 *

 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfServiceContainer implements sfServiceContainerInterface, ArrayAccess, Iterator
{
  protected
    $serviceIds = array(),
    $parameters = array(),
    $services   = array(),
    $count      = 0;

  /**
   * Constructor.
   *
   * @param array $parameters An array of parameters
   */
  public function __construct(array $parameters = array())
  {
    $this->setParameters($parameters);
    $this->setService('service_container', $this);
  }

  /**
   * Sets the service container parameters.
   *
   * @param array $parameters An array of parameters
   */
  public function setParameters(array $parameters)
  {
    $this->parameters = array();
    foreach ($parameters as $key => $value)
    {
      $this->parameters[strtolower($key)] = $value;
    }
  }

  /**
   * Adds parameters to the service container parameters.
   *
   * @param array $parameters An array of parameters
   */
  public function addParameters(array $parameters)
  {
    $this->setParameters(array_merge($this->parameters, $parameters));
  }

  /**
   * Gets the service container parameters.
   *
   * @return array An array of parameters
   */
  public function getParameters()
  {
    return $this->parameters;
  }

  /**
   * Gets a service container parameter.
   *
   * @param  string $name The parameter name
   *
   * @return mixed  The parameter value
   *
   * @throw  InvalidArgumentException if the parameter is not defined
   */
  public function getParameter($name)
  {
    if (!$this->hasParameter($name))
    {
      throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
    }

    return $this->parameters[strtolower($name)];
  }

  /**
   * Sets a service container parameter.
   *
   * @param string $name       The parameter name
   * @param mixed  $parameters The parameter value
   */
  public function setParameter($name, $value)
  {
    $this->parameters[strtolower($name)] = $value;
  }

  /**
   * Returns true if a parameter name is defined.
   *
   * @param  string  $name       The parameter name
   *
   * @return Boolean true if the parameter name is defined, false otherwise
   */
  public function hasParameter($name)
  {
    return array_key_exists(strtolower($name), $this->parameters);
  }

  /**
   * Sets a service.
   *
   * @param string $id      The service identifier
   * @param object $service The service instance
   */
  public function setService($id, $service)
  {
    $this->services[$id] = $service;
  }

  /**
   * Returns true if the given service is defined.
   *
   * @param  string  $id      The service identifier
   *
   * @return Boolean true if the service is defined, false otherwise
   */
  public function hasService($id)
  {
    return isset($this->services[$id]) || method_exists($this, 'get'.self::camelize($id).'Service');
  }

  /**
   * Gets a service.
   *
   * If a service is both defined through a setService() method and
   * with a set*Service() method, the former has always precedence.
   *
   * @param  string $id The service identifier
   *
   * @return object The associated service
   *
   * @throw InvalidArgumentException if the service is not defined
   */
  public function getService($id)
  {
    if (isset($this->services[$id]))
    {
      return $this->services[$id];
    }

    if (method_exists($this, $method = 'get'.self::camelize($id).'Service'))
    {
      return $this->$method();
    }

    throw new InvalidArgumentException(sprintf('The service "%s" does not exist.', $id));
  }

  /**
   * Gets all service ids.
   *
   * @return array An array of all defined service ids
   */
  public function getServiceIds()
  {
    $ids = array();
    $r = new ReflectionClass($this);
    foreach ($r->getMethods() as $method)
    {
      if (preg_match('/^get(.+)Service$/', $name = $method->getName(), $match))
      {
        $ids[] = self::underscore($match[1]);
      }
    }

    return array_merge($ids, array_keys($this->services));
  }

  /**
   * Returns true if the parameter name is defined (implements the ArrayAccess interface).
   *
   * @param  string  The parameter name
   *
   * @return Boolean true if the parameter name is defined, false otherwise
   */
  public function offsetExists($name)
  {
    return $this->hasParameter($name);
  }

  /**
   * Gets a service container parameter (implements the ArrayAccess interface).
   *
   * @param  string The parameter name
   *
   * @return mixed  The parameter value
   */
  public function offsetGet($name)
  {
    return $this->getParameter($name);
  }

  /**
   * Sets a parameter (implements the ArrayAccess interface).
   *
   * @param string The parameter name
   * @param mixed  The parameter value
   */
  public function offsetSet($name, $value)
  {
    $this->setParameter($name, $value);
  }

  /**
   * Removes a parameter (implements the ArrayAccess interface).
   *
   * @param string The parameter name
   */
  public function offsetUnset($name)
  {
    unset($this->parameters[$name]);
  }

  /**
   * Returns true if the container has a service with the given identifier.
   *
   * @param  string  The service identifier
   *
   * @return Boolean true if the container has a service with the given identifier, false otherwise
   */
  public function __isset($id)
  {
    return $this->hasService($id);
  }

  /**
   * Gets the service associated with the given identifier.
   *
   * @param  string The service identifier
   *
   * @return mixed  The service instance associated with the given identifier
   */
  public function __get($id)
  {
    return $this->getService($id);
  }

  /**
   * Sets a service.
   *
   * @param string The service identifier
   * @param mixed  A service instance
   */
  public function __set($id, $service)
  {
    $this->setService($id, $service);
  }

  /**
   * Removes a service by identifier.
   *
   * @param string The service identifier
   */
  public function __unset($id)
  {
    throw new LogicException('You can\'t unset a service.');
  }

  /**
   * Resets the service identifiers array to the beginning (implements the Iterator interface).
   */
  public function rewind()
  {
    $this->serviceIds = $this->getServiceIds();

    $this->count = count($this->serviceIds);
  }

  /**
   * Gets the key associated with the current service (implements the Iterator interface).
   *
   * @return string The service identifier
   */
  public function key()
  {
    return current($this->serviceIds);
  }

  /**
   * Returns the current service (implements the Iterator interface).
   *
   * @return mixed The service
   */
  public function current()
  {
    return $this->getService(current($this->serviceIds));
  }

  /**
   * Moves to the next service (implements the Iterator interface).
   */
  public function next()
  {
    next($this->serviceIds);

    --$this->count;
  }

  /**
   * Returns true if the current service is valid (implements the Iterator interface).
   *
   * @return boolean The validity of the current service; true if it is valid
   */
  public function valid()
  {
    return $this->count > 0;
  }

  static public function camelize($id)
  {
    return preg_replace(array('/(^|_|-)+(.)/e', '/\.(.)/e'), array("strtoupper('\\2')", "'_'.strtoupper('\\1')"), $id);
  }

  static public function underscore($id)
  {
    return strtolower(preg_replace(array('/_/', '/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('.', '\\1_\\2', '\\1_\\2'), $id));
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerLoaderFileXml loads XML files service definitions.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfServiceContainerLoaderFileXml.php 267 2009-03-26 19:56:18Z fabien $
 */
class sfServiceContainerLoaderFileXml extends sfServiceContainerLoaderFile
{
  /**
   * Loads an array of XML files.
   *
   * If multiple files are loaded, the services and parameters are merged.
   *
   * Remember that services and parameters are simple key/pair stores.
   *
   * When overriding a value, the old one is totally replaced, even if it is
   * a "complex" value (an array for instance):
   *
   * <pre>
   *   file1.xml
   *   <parameter key="complex" type="collection">
   *     <parameter>true</parameter>
   *     <parameter>false</parameter>
   *   </parameter>
   *
   *   file2.xml
   *   <parameter key="complex">foo</parameter>
   * </pre>
   *
   * If you load file1.xml and file2.xml in this order, the value of complex
   * will be "foo".
   *
   * @param  array $files An array of XML files
   *
   * @return array An array of definitions and parameters
   */
  public function doLoad($files)
  {
    return $this->parse($this->getFilesAsXml($files));
  }

  protected function parse(array $xmls)
  {
    $parameters = array();
    $definitions = array();

    foreach ($xmls as $file => $xml)
    {
      // create all the anonymous services and give them unique names
      list($anonymousDefinitions, $xml) = $this->processAnonymousServices($xml, $file);
      $definitions = array_merge($definitions, $anonymousDefinitions);

      // imports
      list($importedDefinitions, $importedParameters) = $this->parseImports($xml, $file);
      $definitions = array_merge($definitions, $importedDefinitions);
      $parameters = array_merge($parameters, $importedParameters);

      // parameters
      $parameters = array_merge($parameters, $this->parseParameters($xml, $file));

      // services
      $definitions = array_merge($definitions, $this->parseDefinitions($xml, $file));
    }

    return array($definitions, $parameters);
  }

  protected function parseParameters($xml, $file)
  {
    if (!$xml->parameters)
    {
      return array();
    }

    return $xml->parameters->getArgumentsAsPhp('parameter');
  }

  protected function parseImports($xml, $file)
  {
    if (!$xml->imports)
    {
      return array(array(), array());
    }

    $definitions = array();
    $parameters = array();
    foreach ($xml->imports->import as $import)
    {
      list($importedDefinitions, $importedParameters) = $this->parseImport($import, $file);

      $definitions = array_merge($definitions, $importedDefinitions);
      $parameters = array_merge($parameters, $importedParameters);
    }

    return array($definitions, $parameters);
  }

  protected function parseImport($import, $file)
  {
    if (isset($import['class']) && $import['class'] != get_class($this))
    {
      $class = (string) $import['class'];
      $loader = new $class($this->container, $this->paths);
    }
    else
    {
      $loader = $this;
    }

    $importedFile = $this->getAbsolutePath((string) $import['resource'], dirname($file));

    return call_user_func(array($loader, 'doLoad'), array($importedFile));
  }

  protected function parseDefinitions($xml, $file)
  {
    if (!$xml->services)
    {
      return array();
    }

    $definitions = array();
    foreach ($xml->services->service as $service)
    {
      $definitions[(string) $service['id']] = $this->parseDefinition($service, $file);
    }

    return $definitions;
  }

  protected function parseDefinition($service, $file)
  {
    if ((string) $service['alias'])
    {
      return (string) $service['alias'];
    }

    $definition = new sfServiceDefinition((string) $service['class']);

    foreach (array('shared', 'constructor') as $key)
    {
      $method = 'set'.ucfirst($key);
      if (isset($service[$key]))
      {
        $definition->$method((string) $service->getAttributeAsPhp($key));
      }
    }

    if ($service->file)
    {
      $definition->setFile((string) $service->file);
    }

    $definition->setArguments($service->getArgumentsAsPhp('argument'));

    if (isset($service->configurator))
    {
      if (isset($service->configurator['function']))
      {
        $definition->setConfigurator((string) $service->configurator['function']);
      }
      else
      {
        if (isset($service->configurator['service']))
        {
          $class = new sfServiceReference((string) $service->configurator['service']);
        }
        else
        {
          $class = (string) $service->configurator['class'];
        }

        $definition->setConfigurator(array($class, (string) $service->configurator['method']));
      }
    }

    foreach ($service->call as $call)
    {
      $definition->addMethodCall((string) $call['method'], $call->getArgumentsAsPhp('argument'));
    }

    return $definition;
  }

  protected function getFilesAsXml(array $files)
  {
    $xmls = array();
    foreach ($files as $file)
    {
      $path = $this->getAbsolutePath($file);

      if (!file_exists($path))
      {
        throw new InvalidArgumentException(sprintf('The service file "%s" does not exist.', $file));
      }

      $dom = new DOMDocument();
      libxml_use_internal_errors(true);
      if (!$dom->load($path))
      {
        throw new InvalidArgumentException(implode("\n", $this->getXmlErrors()));
      }
      libxml_use_internal_errors(false);
      $this->validate($dom);

      $xmls[$path] = simplexml_import_dom($dom, 'sfServiceSimpleXMLElement');
    }

    return $xmls;
  }

  protected function processAnonymousServices($xml, $file)
  {
    $definitions = array();
    $count = 0;

    // find anonymous service definitions
    $xml->registerXPathNamespace('container', 'http://symfony-project.org/2.0/container');
    $nodes = $xml->xpath('//container:argument[@type="service"][not(@id)]');
    foreach ($nodes as $node)
    {
      $node['id'] = sprintf('_%s_%d', md5($file), ++$count);
      $definitions[(string) $node['id']] = array($node->service, $file);
      $node->service['id'] = (string) $node['id'];
    }

    // resolve definitions
    krsort($definitions);
    foreach ($definitions as $id => $def)
    {
      $definitions[$id] = $this->parseDefinition($def[0], $def[1]);

      $oNode = dom_import_simplexml($def[0]);
      $oNode->parentNode->removeChild($oNode);
    }

    return array($definitions, $xml);
  }

  protected function validate($dom)
  {
    libxml_use_internal_errors(true);
    if (!$dom->schemaValidate(dirname(__FILE__).'/services.xsd'))
    {
      throw new InvalidArgumentException(implode("\n", $this->getXmlErrors()));
    }
    libxml_use_internal_errors(false);
  }

  protected function getXmlErrors()
  {
    $errors = array();
    foreach (libxml_get_errors() as $error)
    {
      $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
        LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
        $error->code,
        trim($error->message),
        $error->file ? $error->file : 'n/a',
        $error->line,
        $error->column
      );
    }

    libxml_clear_errors();

    return $errors;
  }
}


text:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://symfony-project.org/2.0/container" targetNamespace="http://symfony-project.org/2.0/container" elementFormDefault="qualified">
  <xs:element name="container" type="container" />

  <xs:complexType name="container">
    <xs:sequence>
      <xs:element name="imports" type="imports" minOccurs="0" maxOccurs="1" />
      <xs:element name="parameters" type="parameters" minOccurs="0" maxOccurs="1" />
      <xs:element name="services" type="services" minOccurs="0" maxOccurs="1" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="services">
    <xs:sequence>
      <xs:element name="service" type="service" minOccurs="1" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="imports">
    <xs:sequence>
      <xs:element name="import" type="import" minOccurs="1" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="import">
    <xs:attribute name="resource" type="xs:string" use="required" />
    <xs:attribute name="class" type="xs:string" />
  </xs:complexType>

  <xs:complexType name="configurator">
    <xs:attribute name="id" type="xs:string" />
    <xs:attribute name="service" type="xs:string" />
    <xs:attribute name="class" type="xs:string" />
    <xs:attribute name="method" type="xs:string" />
    <xs:attribute name="function" type="xs:string" />
  </xs:complexType>

  <xs:complexType name="service">
    <xs:choice maxOccurs="unbounded">
      <xs:element name="file" type="xs:string" minOccurs="0" maxOccurs="1" />
      <xs:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
      <xs:element name="configurator" type="configurator" minOccurs="0" maxOccurs="1" />
      <xs:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
    </xs:choice>
    <xs:attribute name="id" type="xs:string" />
    <xs:attribute name="class" type="xs:string" />
    <xs:attribute name="shared" type="boolean" />
    <xs:attribute name="constructor" type="xs:string" />
    <xs:attribute name="alias" type="xs:string" />
  </xs:complexType>

  <xs:complexType name="parameters">
    <xs:sequence>
      <xs:element name="parameter" type="parameter" minOccurs="1" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="type" type="parameter_type" />
    <xs:attribute name="key" type="xs:string" />
  </xs:complexType>

  <xs:complexType name="parameter" mixed="true">
    <xs:sequence>
      <xs:element name="parameter" type="parameter" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="type" type="parameter_type" />
    <xs:attribute name="id" type="xs:string" />
    <xs:attribute name="key" type="xs:string" />
  </xs:complexType>

  <xs:complexType name="argument" mixed="true">
    <xs:choice maxOccurs="unbounded">
      <xs:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
      <xs:element name="service" type="service" />
    </xs:choice>
    <xs:attribute name="type" type="argument_type" />
    <xs:attribute name="id" type="xs:string" />
    <xs:attribute name="key" type="xs:string" />
  </xs:complexType>

  <xs:complexType name="call" mixed="true">
    <xs:choice maxOccurs="unbounded">
      <xs:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
      <xs:element name="service" type="service" />
    </xs:choice>
    <xs:attribute name="method" type="xs:string" />
  </xs:complexType>

  <xs:simpleType name="parameter_type">
    <xs:restriction base="xs:string">
      <xs:enumeration value="collection" />
      <xs:enumeration value="service" />
      <xs:enumeration value="string" />
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="argument_type">
    <xs:restriction base="xs:string">
      <xs:enumeration value="collection" />
      <xs:enumeration value="service" />
      <xs:enumeration value="string" />
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="boolean">
    <xs:restriction base="xs:string">
      <xs:pattern value="(%.+%|true|false|1|0|off|on)" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerDumperYaml dumps a service container as a YAML string.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfServiceContainerDumperYaml extends sfServiceContainerDumper
{
  /**
   * Dumps the service container as an YAML string.
   *
   * @param  array  $options An array of options
   *
   * @return string A YAML string representing of the service container
   */
  public function dump(array $options = array())
  {
    return $this->addParameters()."\n".$this->addServices();
  }

  protected function addService($id, $definition)
  {
    $code = "  $id:\n";
    $code .= sprintf("    class: %s\n", $definition->getClass());

    if ($definition->getFile())
    {
      $code .= sprintf("    file: %s\n", $definition->getFile());
    }

    if ($definition->getConstructor())
    {
      $code .= sprintf("    constructor: %s\n", $definition->getConstructor());
    }

    if ($definition->getArguments())
    {
      $code .= sprintf("    arguments: %s\n", sfYaml::dump($this->dumpValue($definition->getArguments()), 0));
    }

    if ($definition->getMethodCalls())
    {
      $code .= sprintf("    calls:\n      %s\n", str_replace("\n", "\n      ", sfYaml::dump($this->dumpValue($definition->getMethodCalls()), 1)));
    }

    if (!$definition->isShared())
    {
      $code .= "    shared: false\n";
    }

    if ($callable = $definition->getConfigurator())
    {
      if (is_array($callable))
      {
        if (is_object($callable[0]) && $callable[0] instanceof sfServiceReference)
        {
          $callable = array($this->getServiceCall((string) $callable[0]), $callable[1]);
        }
        else
        {
          $callable = array($callable[0], $callable[1]);
        }
      }

      $code .= sprintf("    configurator: %s\n", sfYaml::dump($callable, 0));
    }

    return $code;
  }

  protected function addServiceAlias($alias, $id)
  {
    return sprintf("  %s: @%s\n", $alias, $id);
  }

  protected function addServices()
  {
    if (!$this->container->getServiceDefinitions())
    {
      return '';
    }

    $code = "services:\n";
    foreach ($this->container->getServiceDefinitions() as $id => $definition)
    {
      $code .= $this->addService($id, $definition);
    }

    foreach ($this->container->getAliases() as $alias => $id)
    {
      $code .= $this->addServiceAlias($alias, $id);
    }

    return $code;
  }

  protected function addParameters()
  {
    if (!$this->container->getParameters())
    {
      return '';
    }

    return sfYaml::dump(array('parameters' => $this->prepareParameters($this->container->getParameters())), 2);
  }

  protected function dumpValue($value)
  {
    if (is_array($value))
    {
      $code = array();
      foreach ($value as $k => $v)
      {
        $code[$k] = $this->dumpValue($v);
      }

      return $code;
    }
    elseif (is_object($value) && $value instanceof sfServiceReference)
    {
      return $this->getServiceCall((string) $value);
    }
    elseif (is_object($value) && $value instanceof sfServiceParameter)
    {
      return $this->getParameterCall((string) $value);
    }
    elseif (is_object($value) || is_resource($value))
    {
      throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
    }
    else
    {
      return $value;
    }
  }

  protected function getServiceCall($id)
  {
    return sprintf('@%s', $id);
  }

  protected function getParameterCall($id)
  {
    return sprintf('%%%s%%', $id);
  }

  protected function prepareParameters($parameters)
  {
    $filtered = array();
    foreach ($parameters as $key => $value)
    {
      if (is_array($value))
      {
        $value = $this->prepareParameters($value);
      }
      elseif ($value instanceof sfServiceReference)
      {
        $value = '@'.$value;
      }

      $filtered[$key] = $value;
    }

    return $this->escape($filtered);
  }

  protected function escape($arguments)
  {
    $args = array();
    foreach ($arguments as $k => $v)
    {
      if (is_array($v))
      {
        $args[$k] = $this->escape($v);
      }
      elseif (is_string($v))
      {
        $args[$k] = str_replace('%', '%%', $v);
      }
      else
      {
        $args[$k] = $v;
      }
    }

    return $args;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerLoaderFileXml loads YAML files service definitions.
 *
 * The YAML format does not support anonymous services (cf. the XML loader).
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfServiceContainerLoaderFileYaml.php 269 2009-03-26 20:39:16Z fabien $
 */
class sfServiceContainerLoaderFileYaml extends sfServiceContainerLoaderFile
{
  public function doLoad($files)
  {
    return $this->parse($this->getFilesAsArray($files));
  }

  protected function parse($data)
  {
    $parameters = array();
    $definitions = array();

    foreach ($data as $file => $content)
    {
      // imports
      list($importedDefinitions, $importedParameters) = $this->parseImports($content, $file);
      $definitions = array_merge($definitions, $importedDefinitions);
      $parameters = array_merge($parameters, $importedParameters);

      // parameters
      if (isset($content['parameters']))
      {
        foreach ($content['parameters'] as $key => $value)
        {
          $parameters[strtolower($key)] = $this->resolveServices($value);
        }
      }

      // services
      $definitions = array_merge($definitions, $this->parseDefinitions($content, $file));
    }

    return array($definitions, $parameters);
  }

  protected function parseImports($content, $file)
  {
    if (!isset($content['imports']))
    {
      return array(array(), array());
    }

    $definitions = array();
    $parameters = array();
    foreach ($content['imports'] as $import)
    {
      list($importedDefinitions, $importedParameters) = $this->parseImport($import, $file);

      $definitions = array_merge($definitions, $importedDefinitions);
      $parameters = array_merge($parameters, $importedParameters);
    }

    return array($definitions, $parameters);
  }

  protected function parseImport($import, $file)
  {
    if (isset($import['class']) && $import['class'] != get_class($this))
    {
      $class = $import['class'];
      $loader = new $class($this->container, $this->paths);
    }
    else
    {
      $loader = $this;
    }

    $importedFile = $this->getAbsolutePath($import['resource'], dirname($file));

    return call_user_func(array($loader, 'doLoad'), array($importedFile));
  }

  protected function parseDefinitions($content, $file)
  {
    if (!isset($content['services']))
    {
      return array();
    }

    $definitions = array();
    foreach ($content['services'] as $id => $service)
    {
      $definitions[$id] = $this->parseDefinition($service, $file);
    }

    return $definitions;
  }

  protected function parseDefinition($service, $file)
  {
    if (is_string($service) && 0 === strpos($service, '@'))
    {
      return substr($service, 1);
    }

    $definition = new sfServiceDefinition($service['class']);

    if (isset($service['shared']))
    {
      $definition->setShared($service['shared']);
    }

    if (isset($service['constructor']))
    {
      $definition->setConstructor($service['constructor']);
    }

    if (isset($service['file']))
    {
      $definition->setFile($service['file']);
    }

    if (isset($service['arguments']))
    {
      $definition->setArguments($this->resolveServices($service['arguments']));
    }

    if (isset($service['configurator']))
    {
      if (is_string($service['configurator']))
      {
        $definition->setConfigurator($service['configurator']);
      }
      else
      {
        $definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1]));
      }
    }

    if (isset($service['calls']))
    {
      foreach ($service['calls'] as $call)
      {
        $definition->addMethodCall($call[0], $this->resolveServices($call[1]));
      }
    }

    return $definition;
  }

  protected function getFilesAsArray(array $files)
  {
    $yamls = array();
    foreach ($files as $file)
    {
      $file = $this->getAbsolutePath($file);

      if (!file_exists($file))
      {
        throw new InvalidArgumentException(sprintf('The service file "%s" does not exist.', $file));
      }

      $yamls[$file] = $this->validate(sfYaml::load($file), $file);
    }

    return $yamls;
  }

  protected function validate($content, $file)
  {
    if (null === $content)
    {
      return $content;
    }

    if (!is_array($content))
    {
      throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
    }

    foreach (array_keys($content) as $key)
    {
      if (!in_array($key, array('imports', 'parameters', 'services')))
      {
        throw new InvalidArgumentException(sprintf('The service file "%s" is not valid ("%s" is not recognized).', $file, $key));
      }
    }

    return $content;
  }

  protected function resolveServices($value)
  {
    if (is_array($value))
    {
      $value = array_map(array($this, 'resolveServices'), $value);
    }
    else if (is_string($value) && 0 === strpos($value, '@'))
    {
      $value = new sfServiceReference(substr($value, 1));
    }

    return $value;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerLoader is the abstract class used by all built-in loaders that are file based.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
abstract class sfServiceContainerLoaderFile extends sfServiceContainerLoader
{
  protected
    $paths = array();

  /**
   * Constructor.
   *
   * @param sfServiceContainerBuilder $container A sfServiceContainerBuilder instance
   * @param string|array              $paths     A path or an array of paths where to look for resources
   */
  public function __construct(sfServiceContainerBuilder $container = null, $paths = array())
  {
    parent::__construct($container);

    if (!is_array($paths))
    {
      $paths = array($paths);
    }

    $this->paths = $paths;
  }

  /**
   * Loads a resource.
   *
   * A resource is a file or an array of files.
   *
   * The concrete classes always have access to an array of files
   * as this method converts single file argument to an array.
   *
   * @param mixed $resource The resource path
   */
  public function load($resource)
  {
    if (!is_array($resource))
    {
      $resource = array($resource);
    }

    return parent::load($resource);
  }

  protected function getAbsolutePath($file, $currentPath = null)
  {
    if (self::isAbsolutePath($file))
    {
      return $file;
    }
    else if (null !== $currentPath && file_exists($currentPath.DIRECTORY_SEPARATOR.$file))
    {
      return $currentPath.DIRECTORY_SEPARATOR.$file;
    }
    else
    {
      foreach ($this->paths as $path)
      {
        if (file_exists($path.DIRECTORY_SEPARATOR.$file))
        {
          return $path.DIRECTORY_SEPARATOR.$file;
        }
      }
    }

    return $file;
  }

  static protected function isAbsolutePath($file)
  {
    if ($file[0] == '/' || $file[0] == '\\' ||
        (strlen($file) > 3 && ctype_alpha($file[0]) &&
         $file[1] == ':' &&
         ($file[2] == '\\' || $file[2] == '/')
        )
       )
    {
      return true;
    }

    return false;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerLoader is the abstract class used by all built-in loaders.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfServiceContainerLoader.php 267 2009-03-26 19:56:18Z fabien $
 */
abstract class sfServiceContainerLoader implements sfServiceContainerLoaderInterface
{
  protected $container;

  /**
   * Constructor.
   *
   * @param sfServiceContainerBuilder $container A sfServiceContainerBuilder instance
   */
  public function __construct(sfServiceContainerBuilder $container = null)
  {
    $this->container = $container;
  }

  /**
   * Sets the service container attached to this loader.
   *
   * @param sfServiceContainerBuilder $container A sfServiceContainerBuilder instance
   */
  public function setServiceContainer(sfServiceContainerBuilder $container)
  {
    $this->container = $container;
  }

  /**
   * Loads a resource.
   *
   * A resource can be anything that can be converted to an array of
   * definitions and parameters by the doLoad() method.
   *
   * Service definitions overrides the current defined ones.
   *
   * But for parameters, they are overridden by the current ones. It allows
   * the parameters passed to the container constructor to have precedence
   * over the loaded ones.
   *
   * $container = new sfServiceContainerBuilder(array('foo' => 'bar'));
   * $loader = new sfServiceContainerLoaderXXX($container);
   * $loader->load('resource_name');
   * $container->register('foo', new stdClass());
   *
   * In the above example, even if the loaded resource defines a foo
   * parameter, the value will still be 'bar' as defined in the builder
   * constructor.
   *
   * You can also pass multiple resource paths to the constructor.
   *
   * @param mixed $resource The resource path
   */
  public function load($resource)
  {
    if (!$this->container)
    {
      throw new LogicException('You must attach the loader to a service container.');
    }

    $resources = func_get_args();
    foreach ($resources as $resource)
    {
      list($definitions, $parameters) = $this->doLoad($resource);

      foreach ($definitions as $id => $definition)
      {
        if (is_string($definition))
        {
          $this->container->setAlias($id, $definition);
        }
        else
        {
          $this->container->setServiceDefinition($id, $definition);
        }
      }

      $currentParameters = $this->container->getParameters();
      foreach ($parameters as $key => $value)
      {
        $this->container->setParameter($key, $this->container->resolveValue($value));
      }
      $this->container->addParameters($currentParameters);
    }
  }

  /**
   * Loads a resource.
   *
   * Concrete classes implements this method to convert
   * the resource to an array of definitions and parameters.
   *
   * @param  mixed $resource The resource path
   *
   * @return array An array of definitions and parameters
   */
  abstract public function doLoad($resource);
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerAutoloader is an autoloader for the service container classes.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
class sfServiceContainerAutoloader
{
  /**
   * Registers sfServiceContainerAutoloader as an SPL autoloader.
   */
  static public function register()
  {
    ini_set('unserialize_callback_func', 'spl_autoload_call');
    spl_autoload_register(array(new self, 'autoload'));
  }

  /**
   * Handles autoloading of classes.
   *
   * @param  string  $class  A class name.
   *
   * @return boolean Returns true if the class has been loaded
   */
  public function autoload($class)
  {
    if (0 !== strpos($class, 'sfService'))
    {
      return false;
    }

    require dirname(__FILE__).'/'.$class.'.php';

    return true;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceParameter represents a parameter reference.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfServiceReference.php 267 2009-03-26 19:56:18Z fabien $
 */
class sfServiceParameter
{
  protected
    $id = null;

  /**
   * Constructor.
   *
   * @param string $id The parameter key
   */
  public function __construct($id)
  {
    $this->id = $id;
  }

  /**
   * __toString.
   *
   * @return string The parameter key
   */
  public function __toString()
  {
    return (string) $this->id;
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerBuilder is a DI container that provides an interface to build the services.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfServiceContainerBuilder.php 269 2009-03-26 20:39:16Z fabien $
 */
class sfServiceContainerBuilder extends sfServiceContainer
{
  protected
    $definitions = array(),
    $aliases     = array(),
    $loading     = array();

  /**
   * Sets a service.
   *
   * @param string $id      The service identifier
   * @param object $service The service instance
   */
  public function setService($id, $service)
  {
    unset($this->aliases[$id]);

    parent::setService($id, $service);
  }

  /**
   * Returns true if the given service is defined.
   *
   * @param  string  $id      The service identifier
   *
   * @return Boolean true if the service is defined, false otherwise
   */
  public function hasService($id)
  {
    return isset($this->definitions[$id]) || isset($this->aliases[$id]) || parent::hasService($id);
  }

  /**
   * Gets a service.
   *
   * @param  string $id The service identifier
   *
   * @return object The associated service
   *
   * @throw InvalidArgumentException if the service is not defined
   * @throw LogicException if the service has a circular reference to itself
   */
  public function getService($id)
  {
    try
    {
      return parent::getService($id);
    }
    catch (InvalidArgumentException $e)
    {
      if (isset($this->loading[$id]))
      {
        throw new LogicException(sprintf('The service "%s" has a circular reference to itself.', $id));
      }

      if (!$this->hasServiceDefinition($id) && isset($this->aliases[$id]))
      {
        return $this->getService($this->aliases[$id]);
      }

      $definition = $this->getServiceDefinition($id);

      $this->loading[$id] = true;

      if ($definition->isShared())
      {
        $service = $this->services[$id] = $this->createService($definition);
      }
      else
      {
        $service = $this->createService($definition);
      }

      unset($this->loading[$id]);

      return $service;
    }
  }

  /**
   * Gets all service ids.
   *
   * @return array An array of all defined service ids
   */
  public function getServiceIds()
  {
    return array_unique(array_merge(array_keys($this->getServiceDefinitions()), array_keys($this->aliases), parent::getServiceIds()));
  }

  /**
   * Sets an alias for an existing service.
   *
   * @param string $alias The alias to create
   * @param string $id    The service to alias
   */
  public function setAlias($alias, $id)
  {
    $this->aliases[$alias] = $id;
  }

  /**
   * Gets all defined aliases.
   *
   * @return array An array of aliases
   */
  public function getAliases()
  {
    return $this->aliases;
  }

  /**
   * Registers a service definition.
   *
   * This methods allows for simple registration of service definition
   * with a fluid interface.
   *
   * @param  string $id    The service identifier
   * @param  string $class The service class
   *
   * @return sfServiceDefinition A sfServiceDefinition instance
   */
  public function register($id, $class)
  {
    return $this->setServiceDefinition($id, new sfServiceDefinition($class));
  }

  /**
   * Adds the service definitions.
   *
   * @param array $definitions An array of service definitions
   */
  public function addServiceDefinitions(array $definitions)
  {
    foreach ($definitions as $id => $definition)
    {
      $this->setServiceDefinition($id, $definition);
    }
  }

  /**
   * Sets the service definitions.
   *
   * @param array $definitions An array of service definitions
   */
  public function setServiceDefinitions(array $definitions)
  {
    $this->definitions = array();
    $this->addServiceDefinitions($definitions);
  }

  /**
   * Gets all service definitions.
   *
   * @return array An array of sfServiceDefinition instances
   */
  public function getServiceDefinitions()
  {
    return $this->definitions;
  }

  /**
   * Sets a service definition.
   *
   * @param  string              $id         The service identifier
   * @param  sfServiceDefinition $definition A sfServiceDefinition instance
   */
  public function setServiceDefinition($id, sfServiceDefinition $definition)
  {
    unset($this->aliases[$id]);

    return $this->definitions[$id] = $definition;
  }

  /**
   * Returns true if a service definition exists under the given identifier.
   *
   * @param  string  $id The service identifier
   *
   * @return Boolean true if the service definition exists, false otherwise
   */
  public function hasServiceDefinition($id)
  {
    return array_key_exists($id, $this->definitions);
  }

  /**
   * Gets a service definition.
   *
   * @param  string  $id The service identifier
   *
   * @return sfServiceDefinition A sfServiceDefinition instance
   *
   * @throw InvalidArgumentException if the service definition does not exist
   */
  public function getServiceDefinition($id)
  {
    if (!$this->hasServiceDefinition($id))
    {
      throw new InvalidArgumentException(sprintf('The service definition "%s" does not exist.', $id));
    }

    return $this->definitions[$id];
  }

  /**
   * Creates a service for a service definition.
   *
   * @param  sfServiceDefinition $definition A service definition instance
   *
   * @return object              The service described by the service definition
   */
  protected function createService(sfServiceDefinition $definition)
  {
    if (null !== $definition->getFile())
    {
      require_once $this->resolveValue($definition->getFile());
    }

    $r = new ReflectionClass($this->resolveValue($definition->getClass()));

    $arguments = $this->resolveServices($this->resolveValue($definition->getArguments()));

    if (null !== $definition->getConstructor())
    {
      $service = call_user_func_array(array($this->resolveValue($definition->getClass()), $definition->getConstructor()), $arguments);
    }
    else
    {
      $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
    }

    foreach ($definition->getMethodCalls() as $call)
    {
      call_user_func_array(array($service, $call[0]), $this->resolveServices($this->resolveValue($call[1])));
    }

    if ($callable = $definition->getConfigurator())
    {
      if (is_array($callable) && is_object($callable[0]) && $callable[0] instanceof sfServiceReference)
      {
        $callable[0] = $this->getService((string) $callable[0]);
      }
      elseif (is_array($callable))
      {
        $callable[0] = $this->resolveValue($callable[0]);
      }

      if (!is_callable($callable))
      {
        throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service)));
      }

      call_user_func($callable, $service);
    }

    return $service;
  }

  /**
   * Replaces parameter placeholders (%name%) by their values.
   *
   * @param  mixed $value A value
   *
   * @return mixed The same value with all placeholders replaced by their values
   *
   * @throw RuntimeException if a placeholder references a parameter that does not exist
   */
  public function resolveValue($value)
  {
    if (is_array($value))
    {
      $args = array();
      foreach ($value as $k => $v)
      {
        $args[$this->resolveValue($k)] = $this->resolveValue($v);
      }

      $value = $args;
    }
    else if (is_string($value))
    {
      if (preg_match('/^%([^%]+)%$/', $value, $match))
      {
        // we do this to deal with non string values (boolean, integer, ...)
        // the preg_replace_callback converts them to strings
        if (!$this->hasParameter($name = strtolower($match[1])))
        {
          throw new RuntimeException(sprintf('The parameter "%s" must be defined.', $name));
        }

        $value = $this->getParameter($name);
      }
      else
      {
        $value = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', array($this, 'replaceParameter'), $value));
      }
    }

    return $value;
  }

  /**
   * Replaces service references by the real service instance.
   *
   * @param  mixed $value A value
   *
   * @return mixed The same value with all service references replaced by the real service instances
   */
  public function resolveServices($value)
  {
    if (is_array($value))
    {
      $value = array_map(array($this, 'resolveServices'), $value);
    }
    else if (is_object($value) && $value instanceof sfServiceReference)
    {
      $value = $this->getService((string) $value);
    }

    return $value;
  }

  protected function replaceParameter($match)
  {
    if (!$this->hasParameter($name = strtolower($match[2])))
    {
      throw new RuntimeException(sprintf('The parameter "%s" must be defined.', $name));
    }

    return $this->getParameter($name);
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerLoaderInterface is the interface implemented by service container loader classes.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
interface sfServiceContainerLoaderInterface
{
  function load($resource);
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

/**
 * sfServiceContainerDumper is the abstract class for all built-in dumpers.
 *
 * @package    symfony
 * @subpackage dependency_injection
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id$
 */
abstract class sfServiceContainerDumper implements sfServiceContainerDumperInterface
{
  protected $container;

  /**
   * Constructor.
   *
   * @param sfServiceContainerBuilder $container The service container to dump
   */
  public function __construct(sfServiceContainerBuilder $container)
  {
    $this->container = $container;
  }

  /**
   * Dumps the service container.
   *
   * @param  array  $options An array of options
   *
   * @return string The representation of the service container
   */
  public function dump(array $options = array())
  {
    throw new LogicException('You must extend this abstract class and implement the dump() method.');
  }
}


text:
<?php

/*
 * This file is part of the symfony framework.
 *
 * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

class sfServiceSimpleXMLElement extends SimpleXMLElement
{
  public function getAttributeAsPhp($name)
  {
    return self::phpize($this[$name]);
  }

  public function getArgumentsAsPhp($name)
  {
    $arguments = array();
    foreach ($this->$name as $arg)
    {
      $key = isset($arg['key']) ? (string) $arg['key'] : (!$arguments ? 0 : max(array_keys($arguments)) + 1);

      // parameter keys are case insensitive
      if ('parameter' == $name)
      {
        $key = strtolower($key);
      }

      switch ($arg['type'])
      {
        case 'service':
          $arguments[$key] = new sfServiceReference((string) $arg['id']);
          break;
        case 'collection':
          $arguments[$key] = $arg->getArgumentsAsPhp($name);
          break;
        case 'string':
          $arguments[$key] = (string) $arg;
          break;
        default:
          $arguments[$key] = self::phpize($arg);
      }
    }

    return $arguments;
  }

  static public function phpize($value)
  {
    $value = (string) $value;

    switch (true)
    {
      case 'null' == strtolower($value):
        return null;
      case ctype_digit($value):
        return '0' == $value[0] ? octdec($value) : intval($value);
      case in_array(strtolower($value), array('true', 'on')):
        return true;
      case in_array(strtolower($value), array('false', 'off')):
        return false;
      case is_numeric($value):
        return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value);
      case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $value):
        return floatval(str_replace(',', '', $value));
      default:
        return (string) $value;
    }
  }
}