refactored bundle management

Before I explain the changes, let's talk about the current state.

Before this patch, the registerBundleDirs() method returned an ordered (for
resource overloading) list of namespace prefixes and the path to their
location. Here are some problems with this approach:

 * The paths set by this method and the paths configured for the autoloader
   can be disconnected (leading to unexpected behaviors);

 * A bundle outside these paths worked, but unexpected behavior can occur;

 * Choosing a bundle namespace was limited to the registered namespace
   prefixes, and their number should stay low enough (for performance reasons)
   -- moreover the current Bundle\ and Application\ top namespaces does not
   respect the standard rules for namespaces (first segment should be the
   vendor name);

 * Developers must understand the concept of "namespace prefixes" to
   understand the overloading mechanism, which is one more thing to learn,
   which is Symfony specific;

 * Each time you want to get a resource that can be overloaded (a template for
   instance), Symfony would have tried all namespace prefixes one after the
   other until if finds a matching file. But that can be computed in advance
   to reduce the overhead.

Another topic which was not really well addressed is how you can reference a
file/resource from a bundle (and take into account the possibility of
overloading). For instance, in the routing, you can import a file from a
bundle like this:

  <import resource="FrameworkBundle/Resources/config/internal.xml" />

Again, this works only because we have a limited number of possible namespace
prefixes.

This patch addresses these problems and some more.

First, the registerBundleDirs() method has been removed. It means that you are
now free to use any namespace for your bundles. No need to have specific
prefixes anymore. You are also free to store them anywhere, in as many
directories as you want. You just need to be sure that they are autoloaded
correctly.

The bundle "name" is now always the short name of the bundle class (like
FrameworkBundle or SensioCasBundle). As the best practice is to prefix the
bundle name with the vendor name, it's up to the vendor to ensure that each
bundle name is unique. I insist that a bundle name must be unique. This was
the opposite before as two bundles with the same name was how Symfony2 found
inheritance.

A new getParent() method has been added to BundleInterface. It returns the
bundle name that the bundle overrides (this is optional of course). That way,
there is no ordering problem anymore as the inheritance tree is explicitely
defined by the bundle themselves.

So, with this system, we can easily have an inheritance tree like the
following:

FooBundle < MyFooBundle < MyCustomFooBundle

MyCustomFooBundle returns MyFooBundle for the getParent() method, and
MyFooBundle returns FooBundle.

If two bundles override the same bundle, an exception is thrown.

Based on the bundle name, you can now reference any resource with this
notation:

    @FooBundle/Resources/config/routing.xml
    @FooBundle/Controller/FooController.php

This notation is the input of the Kernel::locateResource() method, which
returns the location of the file (and of course it takes into account
overloading).

So, in the routing, you can now use the following:

    <import resource="@FrameworkBundle/Resources/config/internal.xml" />

The template loading mechanism also use this method under the hood.

As a bonus, all the code that converts from internal notations to file names
(controller names: ControllerNameParser, template names: TemplateNameParser,
resource paths, ...) is now contained in several well-defined classes. The
same goes for the code that look for templates (TemplateLocator), routing
files (FileLocator), ...

As a side note, it is really easy to also support multiple-inheritance for a
bundle (for instance if a bundle returns an array of bundle names it extends).
However, this is not implemented in this patch as I'm not sure we want to
support that.

How to upgrade:

 * Each bundle must now implement two new mandatory methods: getPath() and
   getNamespace(), and optionally the getParent() method if the bundle extends
   another one. Here is a common implementation for these methods:

    /**
     * {@inheritdoc}
     */
    public function getParent()
    {
        return 'MyFrameworkBundle';
    }

    /**
     * {@inheritdoc}
     */
    public function getNamespace()
    {
        return __NAMESPACE__;
    }

    /**
     * {@inheritdoc}
     */
    public function getPath()
    {
        return strtr(__DIR__, '\\', '/');
    }

 * The registerBundleDirs() can be removed from your Kernel class;

 * If your code relies on getBundleDirs() or the kernel.bundle_dirs parameter,
   it should be upgraded to use the new interface (see Doctrine commands for
   many example of such a change);

 * When referencing a bundle, you must now always use its name (no more \ or /
   in bundle names) -- this transition was already done for most things
   before, and now applies to the routing as well;

 * Imports in routing files must be changed:
    Before: <import resource="Sensio/CasBundle/Resources/config/internal.xml" />
    After:  <import resource="@SensioCasBundle/Resources/config/internal.xml" />
This commit is contained in:
Fabien Potencier 2011-01-18 10:23:49 +01:00
parent 252918beb2
commit 6d1e91a1fa
92 changed files with 1404 additions and 896 deletions

View File

@ -20,4 +20,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class CompatAssetsBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -83,8 +83,20 @@ abstract class AbstractDoctrineExtension extends Extension
}
if ($mappingConfig['is_bundle']) {
$namespace = $this->getBundleNamespace($mappingName, $container);
$mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $namespace, $mappingName, $container);
$bundle = null;
foreach ($container->getParameter('kernel.bundles') as $name => $class) {
if ($mappingName === $name) {
$bundle = new \ReflectionClass($class);
break;
}
}
if (null === $bundle) {
throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.', $mappingName));
}
$mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $bundle, $container);
if (!$mappingConfig) {
continue;
}
@ -135,75 +147,25 @@ abstract class AbstractDoctrineExtension extends Extension
}
}
/**
* Finds the bundle directory for a namespace.
*
* If the namespace does not yield a direct match, this method will attempt
* to match parent namespaces exhaustively.
*
* @param string $namespace A bundle namespace omitting the bundle name part
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @return string|false The bundle directory if found, false otherwise
*/
protected function findBundleDirForNamespace($namespace, $container)
{
$bundleDirs = $container->getParameter('kernel.bundle_dirs');
$segment = $namespace;
do {
if (isset($bundleDirs[$segment])) {
return $bundleDirs[$segment] . str_replace('\\', '/', substr($namespace, strlen($segment)));
}
} while ($segment = substr($segment, 0, ($pos = strrpos($segment, '\\'))));
return false;
}
/**
* Get the namespace a bundle resides into.
*
* @param string $bundleName
* @param ContainerBuilder $container
* @return string
*/
protected function getBundleNamespace($bundleName, $container)
{
foreach ($container->getParameter('kernel.bundles') AS $bundleClassName) {
$tmp = dirname(str_replace('\\', '/', $bundleClassName));
$namespace = str_replace('/', '\\', dirname($tmp));
$actualBundleName = basename($tmp);
if ($actualBundleName == $bundleName) {
return $namespace;
}
}
return null;
}
/**
* If this is a bundle controlled mapping all the missing information can be autodetected by this method.
*
* Returns false when autodetection failed, an array of the completed information otherwise.
*
* @param array $bundleConfig
* @param string $namespace
* @param string $bundleName
* @param Container $container
* @param array $bundleConfig
* @param \ReflectionClass $bundle
* @param Container $container
*
* @return array|false
*/
protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, $namespace, $bundleName, $container)
protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, $container)
{
$bundleDir = $this->findBundleDirForNamespace($namespace, $container);
if (!$bundleDir) {
// skip this bundle if we cannot find its location, it must be misspelled or something.
return false;
}
$bundleDir = dirname($bundle->getFilename());
if (!$bundleConfig['type']) {
$bundleConfig['type'] = $this->detectMetadataDriver($bundleDir.'/'.$bundleName, $container);
$bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container);
}
if (!$bundleConfig['type']) {
// skip this bundle, no mapping information was found.
return false;
@ -211,16 +173,16 @@ abstract class AbstractDoctrineExtension extends Extension
if (!$bundleConfig['dir']) {
if (in_array($bundleConfig['type'], array('annotation', 'static-php'))) {
$bundleConfig['dir'] = $bundleDir.'/'.$bundleName.'/' . $this->getMappingObjectDefaultName();
$bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName();
} else {
$bundleConfig['dir'] = $bundleDir.'/'.$bundleName.'/' . $this->getMappingResourceConfigDirectory();
$bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory();
}
} else {
$bundleConfig['dir'] = $bundleDir.'/'.$bundleName.'/' . $bundleConfig['dir'];
$bundleConfig['dir'] = $bundleDir.'/'.$bundleConfig['dir'];
}
if (!$bundleConfig['prefix']) {
$bundleConfig['prefix'] = $namespace.'\\'. $bundleName . '\\' . $this->getMappingObjectDefaultName();
$bundleConfig['prefix'] = $bundle->getNamespaceName().'\\'.$this->getMappingObjectDefaultName();
}
return $bundleConfig;
}
@ -325,7 +287,7 @@ abstract class AbstractDoctrineExtension extends Extension
// add the directory itself as a resource
$container->addResource(new FileResource($dir));
if (is_dir($dir . '/' . $this->getMappingObjectDefaultName())) {
if (is_dir($dir.'/'.$this->getMappingObjectDefaultName())) {
return 'annotation';
}

View File

@ -43,7 +43,7 @@ class ConvertDoctrine1SchemaDoctrineCommand extends DoctrineCommand
->setHelp(<<<EOT
The <info>doctrine:mapping:convert-d1-schema</info> command converts a Doctrine 1 schema to Doctrine 2 mapping files:
<info>./app/console doctrine:mapping:convert-d1-schema /path/to/doctrine1schema "Bundle\MyBundle" xml</info>
<info>./app/console doctrine:mapping:convert-d1-schema /path/to/doctrine1schema "BundleMyBundle" xml</info>
Each Doctrine 1 model will have its own XML mapping file located in <info>Bundle/MyBundle/config/doctrine/metadata</info>.
EOT
@ -55,22 +55,9 @@ EOT
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$bundleClass = null;
$bundleDirs = $this->container->get('kernel')->getBundleDirs();
foreach ($this->container->get('kernel')->getBundles() as $bundle) {
if (strpos(get_class($bundle), $input->getArgument('bundle')) !== false) {
$tmp = dirname(str_replace('\\', '/', get_class($bundle)));
$namespace = str_replace('/', '\\', dirname($tmp));
$class = basename($tmp);
if (isset($bundleDirs[$namespace])) {
$destPath = realpath($bundleDirs[$namespace]).'/'.$class;
$bundleClass = $class;
break;
}
}
}
$bundle = $this->application->getKernel()->getBundle($input->getArgument('bundle'));
$destPath = $bundle->getPath();
$type = $input->getArgument('mapping-type') ? $input->getArgument('mapping-type') : 'xml';
if ('annotation' === $type) {
$destPath .= '/Entity';
@ -98,7 +85,7 @@ EOT
$output->writeln(sprintf('Converting Doctrine 1 schema "<info>%s</info>"', $input->getArgument('d1-schema')));
foreach ($metadata as $class) {
$className = $class->name;
$class->name = $namespace.'\\'.$bundleClass.'\\Entity\\'.$className;
$class->name = $bundle->getNamespace().'\\Entity\\'.$className;
if ('annotation' === $type) {
$path = $destPath.'/'.$className.'.php';
} else {

View File

@ -62,10 +62,10 @@ EOT
}
$entityGenerator = $this->getEntityGenerator();
foreach ($this->container->get('kernel')->getBundles() as $bundle) {
foreach ($this->application->getKernel()->getBundles() as $bundle) {
// retrieve the full bundle classname
$class = $bundle->getReflection()->getName();
$class = get_class($bundle);
if ($filterBundle && $filterBundle != $class) {
continue;

View File

@ -40,13 +40,13 @@ class GenerateEntityDoctrineCommand extends DoctrineCommand
->setHelp(<<<EOT
The <info>doctrine:generate:entity</info> task initializes a new Doctrine entity inside a bundle:
<info>./app/console doctrine:generate:entity "Bundle\MyCustomBundle" "User\Group"</info>
<info>./app/console doctrine:generate:entity "MyCustomBundle" "User\Group"</info>
The above would initialize a new entity in the following entity namespace <info>Bundle\MyCustomBundle\Entity\User\Group</info>.
You can also optionally specify the fields you want to generate in the new entity:
<info>./app/console doctrine:generate:entity "Bundle\MyCustomBundle" "User\Group" --fields="name:string(255) description:text"</info>
<info>./app/console doctrine:generate:entity "MyCustomBundle" "User\Group" --fields="name:string(255) description:text"</info>
EOT
);
}
@ -56,23 +56,10 @@ EOT
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!preg_match('/Bundle$/', $bundle = $input->getArgument('bundle'))) {
throw new \InvalidArgumentException('The bundle name must end with Bundle. Example: "Bundle\MySampleBundle".');
}
$dirs = $this->container->get('kernel')->getBundleDirs();
$tmp = str_replace('\\', '/', $bundle);
$namespace = str_replace('/', '\\', dirname($tmp));
$bundle = basename($tmp);
if (!isset($dirs[$namespace])) {
throw new \InvalidArgumentException(sprintf('Unable to initialize the bundle entity (%s not defined).', $namespace));
}
$bundle = $this->application->getKernel()->getBundle($input->getArgument('bundle'));
$entity = $input->getArgument('entity');
$entityNamespace = $namespace.'\\'.$bundle.'\\Entity';
$fullEntityClassName = $entityNamespace.'\\'.$entity;
$fullEntityClassName = $bundle->getNamespace().'\\Entity\\'.$entity;
$mappingType = $input->getOption('mapping-type');
$class = new ClassMetadataInfo($fullEntityClassName);
@ -103,22 +90,21 @@ EOT
$exporter = $cme->getExporter($mappingType);
if ('annotation' === $mappingType) {
$path = $dirs[$namespace].'/'.$bundle.'/Entity/'.str_replace($entityNamespace.'\\', null, $fullEntityClassName).'.php';
$path = $bundle->getPath().'/Entity/'.$entity.'.php';
$exporter->setEntityGenerator($this->getEntityGenerator());
} else {
$mappingType = 'yaml' == $mappingType ? 'yml' : $mappingType;
$path = $dirs[$namespace].'/'.$bundle.'/Resources/config/doctrine/metadata/orm/'.str_replace('\\', '.', $fullEntityClassName).'.dcm.'.$mappingType;
$path = $bundle->getPath().'/Resources/config/doctrine/metadata/orm/'.str_replace('\\', '.', $fullEntityClassName).'.dcm.'.$mappingType;
}
$code = $exporter->exportClassMetadata($class);
$output->writeln(sprintf('Generating entity for "<info>%s</info>"', $bundle->getName()));
$output->writeln(sprintf(' > generating <comment>%s</comment>', $fullEntityClassName));
if (!is_dir($dir = dirname($path))) {
mkdir($dir, 0777, true);
}
$output->writeln(sprintf('Generating entity for "<info>%s</info>"', $bundle));
$output->writeln(sprintf(' > generating <comment>%s</comment>', $fullEntityClassName));
file_put_contents($path, $code);
}
}

View File

@ -40,22 +40,14 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
$generator = new EntityRepositoryGenerator();
$kernel = $this->application->getKernel();
$bundleDirs = $kernel->getBundleDirs();
foreach ($kernel->getBundles() as $bundle) {
$tmp = dirname(str_replace('\\', '/', get_class($bundle)));
$namespace = str_replace('/', '\\', dirname($tmp));
$class = basename($tmp);
if (isset($bundleDirs[$namespace])) {
$destination = realpath($bundleDirs[$namespace].'/..');
if ($metadatas = $this->getBundleMetadatas($bundle)) {
$output->writeln(sprintf('Generating entity repositories for "<info>%s</info>"', $class));
foreach ($metadatas as $metadata) {
if ($metadata->customRepositoryClassName) {
$output->writeln(sprintf(' > generating <comment>%s</comment>', $metadata->customRepositoryClassName));
$generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destination);
}
foreach ($this->application->getKernel()->getBundles() as $bundle) {
$destination = $bundle->getPath();
if ($metadatas = $this->getBundleMetadatas($bundle)) {
$output->writeln(sprintf('Generating entity repositories for "<info>%s</info>"', get_class($bundle)));
foreach ($metadatas as $metadata) {
if ($metadata->customRepositoryClassName) {
$output->writeln(sprintf(' > generating <comment>%s</comment>', $metadata->customRepositoryClassName));
$generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destination);
}
}
}

View File

@ -42,33 +42,20 @@ class ImportMappingDoctrineCommand extends DoctrineCommand
->setHelp(<<<EOT
The <info>doctrine:mapping:import</info> command imports mapping information from an existing database:
<info>./app/console doctrine:mapping:import "Bundle\MyCustomBundle" xml</info>
<info>./app/console doctrine:mapping:import "MyCustomBundle" xml</info>
You can also optionally specify which entity manager to import from with the <info>--em</info> option:
<info>./app/console doctrine:mapping:import "Bundle\MyCustomBundle" xml --em=default</info>
<info>./app/console doctrine:mapping:import "MyCustomBundle" xml --em=default</info>
EOT
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$bundleClass = null;
$bundleDirs = $this->container->get('kernel')->getBundleDirs();
foreach ($this->container->get('kernel')->getBundles() as $bundle) {
if (strpos(get_class($bundle), $input->getArgument('bundle')) !== false) {
$tmp = dirname(str_replace('\\', '/', get_class($bundle)));
$namespace = str_replace('/', '\\', dirname($tmp));
$class = basename($tmp);
if (isset($bundleDirs[$namespace])) {
$destPath = realpath($bundleDirs[$namespace]).'/'.$class;
$bundleClass = $class;
break;
}
}
}
$bundle = $this->application->getKernel()->getBundle($input->getArgument('bundle'));
$destPath = $bundle->getPath();
$type = $input->getArgument('mapping-type') ? $input->getArgument('mapping-type') : 'xml';
if ('annotation' === $type) {
$destPath .= '/Entity';
@ -100,7 +87,7 @@ EOT
$output->writeln(sprintf('Importing mapping information from "<info>%s</info>" entity manager', $emName));
foreach ($metadata as $class) {
$className = $class->name;
$class->name = $namespace.'\\'.$bundleClass.'\\Entity\\'.$className;
$class->name = $bundle->getNamespace().'\\Entity\\'.$className;
if ('annotation' === $type) {
$path = $destPath.'/'.$className.'.php';
} else {

View File

@ -67,15 +67,8 @@ EOT
$paths = is_array($dirOrFile) ? $dirOrFile : array($dirOrFile);
} else {
$paths = array();
$bundleDirs = $this->container->get('kernel')->getBundleDirs();
foreach ($this->container->get('kernel')->getBundles() as $bundle) {
$tmp = dirname(str_replace('\\', '/', get_class($bundle)));
$namespace = str_replace('/', '\\', dirname($tmp));
$class = basename($tmp);
if (isset($bundleDirs[$namespace]) && is_dir($dir = $bundleDirs[$namespace].'/'.$class.'/DataFixtures/ORM')) {
$paths[] = $dir;
}
foreach ($this->application->getKernel()->getBundles() as $bundle) {
$paths[] = $bundle->getPath().'/DataFixtures/ORM';
}
}

View File

@ -32,4 +32,20 @@ class DoctrineBundle extends Bundle
$container->addCompilerPass(new RegisterEventListenersAndSubscribersPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION);
$container->addCompilerPass(new CreateProxyDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING);
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -21,13 +21,7 @@ class ContainerTest extends TestCase
public function getContainer()
{
$container = new ContainerBuilder(new ParameterBag(array(
'kernel.bundle_dirs' => array(
'DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles' =>
__DIR__ . "/DependencyInjection/Fixtures/Bundles"
),
'kernel.bundles' => array(
'DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\YamlBundle\YamlBundle',
),
'kernel.bundles' => array('YamlBundle' => 'DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\YamlBundle\YamlBundle'),
'kernel.cache_dir' => sys_get_temp_dir(),
)));
$loader = new DoctrineExtension();

View File

@ -428,7 +428,7 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
public function testMultipleOrmLoadCalls()
{
$container = $this->getContainer('AnnotationsBundle');
$container = $this->getContainer(array('XmlBundle', 'AnnotationsBundle'));
$loader = new DoctrineExtension();
$loader->dbalLoad(array(), $container);
@ -447,7 +447,7 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
'DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\AnnotationsBundle\Entity'
));
$this->assertDICDefinitionMethodCallAt(1, $definition, 'addDriver', array(
new Reference('doctrine.orm.default_annotation_metadata_driver'),
new Reference('doctrine.orm.default_xml_metadata_driver'),
'DoctrineBundle\Tests\DependencyInjection\Fixtures\Bundles\XmlBundle\Entity'
));
@ -625,15 +625,15 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
$bundles = array($bundles);
}
foreach ($bundles AS $key => $bundle) {
$map = array();
foreach ($bundles as $bundle) {
require_once __DIR__.'/Fixtures/Bundles/'.($vendor ? $vendor.'/' : '').$bundle.'/'.$bundle.'.php';
$bundles[$key] = 'DoctrineBundle\\Tests\DependencyInjection\\Fixtures\\Bundles\\'.($vendor ? $vendor.'\\' : '').$bundle.'\\'.$bundle;
$map[$bundle] = 'DoctrineBundle\\Tests\DependencyInjection\\Fixtures\\Bundles\\'.($vendor ? $vendor.'\\' : '').$bundle.'\\'.$bundle;
}
return new ContainerBuilder(new ParameterBag(array(
'kernel.bundle_dirs' => array('DoctrineBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles' => __DIR__.'/Fixtures/Bundles'),
'kernel.bundles' => $bundles,
'kernel.bundles' => $map,
'kernel.cache_dir' => sys_get_temp_dir(),
'kernel.root_dir' => __DIR__ . "/../../../../../" // src dir
)));

View File

@ -6,5 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class AnnotationsBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -6,5 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class AnnotationsBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -6,5 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class XmlBundle extends Bundle
{
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -6,5 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class YamlBundle extends Bundle
{
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -26,17 +26,13 @@ abstract class DoctrineCommand extends BaseCommand
public static function configureMigrationsForBundle(Application $application, $bundle, Configuration $configuration)
{
$configuration->setMigrationsNamespace($bundle.'\DoctrineMigrations');
$dirs = $application->getKernel()->getBundleDirs();
$tmp = str_replace('\\', '/', $bundle);
$namespace = str_replace('/', '\\', dirname($tmp));
$bundle = basename($tmp);
$dir = $dirs[$namespace].'/'.$bundle.'/DoctrineMigrations';
$bundle = $this->application->getKernel()->getBundle($input->getArgument('bundle'));
$dir = $bundle->getPath().'/DoctrineMigrations';
$configuration->setMigrationsDirectory($dir);
$configuration->registerMigrationsFromDirectory($dir);
$configuration->setName($bundle.' Migrations');
$configuration->setMigrationsTableName(Inflector::tableize($bundle).'_migration_versions');
$configuration->setMigrationsTableName(Inflector::tableize($bundle->getName()).'_migration_versions');
}
}

View File

@ -21,4 +21,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class DoctrineMigrationsBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -67,15 +67,8 @@ EOT
$paths = is_array($dirOrFile) ? $dirOrFile : array($dirOrFile);
} else {
$paths = array();
$bundleDirs = $this->container->get('kernel')->getBundleDirs();
foreach ($this->container->get('kernel')->getBundles() as $bundle) {
$tmp = dirname(str_replace('\\', '/', get_class($bundle)));
$namespace = str_replace('/', '\\', dirname($tmp));
$class = basename($tmp);
if (isset($bundleDirs[$namespace]) && is_dir($dir = $bundleDirs[$namespace].'/'.$class.'/DataFixtures/MongoDB')) {
$paths[] = $dir;
}
$paths[] = $bundle->getPath().'/DataFixtures/MongoDB';
}
}

View File

@ -35,4 +35,20 @@ class DoctrineMongoDBBundle extends Bundle
$container->addCompilerPass(new CreateProxyDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new CreateHydratorDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING);
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -20,9 +20,10 @@ class ContainerTest extends TestCase
{
public function getContainer()
{
require_once __DIR__.'/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php';
$container = new ContainerBuilder(new ParameterBag(array(
'kernel.bundle_dirs' => array(),
'kernel.bundles' => array(),
'kernel.bundles' => array('YamlBundle' => 'DoctrineMongoDBBundle\Tests\DependencyInjection\Fixtures\Bundles\YamlBundle\YamlBundle'),
'kernel.cache_dir' => sys_get_temp_dir(),
)));
$loader = new DoctrineMongoDBExtension();

View File

@ -344,8 +344,7 @@ abstract class AbstractMongoDBExtensionTest extends TestCase
require_once __DIR__.'/Fixtures/Bundles/'.$bundle.'/'.$bundle.'.php';
return new ContainerBuilder(new ParameterBag(array(
'kernel.bundle_dirs' => array('DoctrineMongoDBBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles' => __DIR__.'/Fixtures/Bundles'),
'kernel.bundles' => array('DoctrineMongoDBBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles\\'.$bundle.'\\'.$bundle),
'kernel.bundles' => array($bundle => 'DoctrineMongoDBBundle\\Tests\\DependencyInjection\\Fixtures\\Bundles\\'.$bundle.'\\'.$bundle),
'kernel.cache_dir' => sys_get_temp_dir(),
)));
}

View File

@ -6,4 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class AnnotationsBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -6,5 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class XmlBundle extends Bundle
{
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -6,5 +6,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class YamlBundle extends Bundle
{
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -33,6 +33,7 @@ class InitBundleCommand extends Command
$this
->setDefinition(array(
new InputArgument('namespace', InputArgument::REQUIRED, 'The namespace of the bundle to create'),
new InputArgument('dir', InputArgument::REQUIRED, 'The directory where to create the bundle'),
))
->setName('init:bundle')
;
@ -50,21 +51,8 @@ class InitBundleCommand extends Command
throw new \InvalidArgumentException('The namespace must end with Bundle.');
}
$dirs = $this->container->get('kernel')->getBundleDirs();
$tmp = str_replace('\\', '/', $namespace);
$namespace = str_replace('/', '\\', dirname($tmp));
$bundle = basename($tmp);
if (!isset($dirs[$namespace])) {
throw new \InvalidArgumentException(sprintf(
"Unable to initialize the bundle (%s is not a defined namespace).\n" .
"Defined namespaces are: %s",
$namespace, implode(', ', array_keys($dirs))));
}
$dir = $dirs[$namespace];
$output->writeln(sprintf('Initializing bundle "<info>%s</info>" in "<info>%s</info>"', $bundle, realpath($dir)));
$dir = $input->getArgument('dir');
$output->writeln(sprintf('Initializing bundle "<info>%s</info>" in "<info>%s</info>"', $bundle, $dir));
if (file_exists($targetDir = $dir.'/'.$bundle)) {
throw new \RuntimeException(sprintf('Bundle "%s" already exists.', $bundle));

View File

@ -1,143 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
/**
* ControllerNameConverter converts controller from the short notation a:b:c
* (BlogBundle:Post:index) to a fully-qualified class::method string
* (Bundle\BlogBundle\Controller\PostController::indexAction); and the other
* way around.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ControllerNameConverter
{
protected $kernel;
protected $logger;
/**
* Constructor.
*
* @param Kernel $kernel A Kernel instance
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(Kernel $kernel, LoggerInterface $logger = null)
{
$this->kernel = $kernel;
$this->logger = $logger;
$this->namespaces = array_keys($kernel->getBundleDirs());
}
/**
* Converts a class::method string to the short notation a:b:c.
*
* @param string $controller A controler (class::method)
*
* @return string A short notation controller (a:b:c)
*/
public function toShortNotation($controller)
{
if (2 != count($parts = explode('::', $controller))) {
throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid class::method controller string.', $controller));
}
list($class, $method) = $parts;
if ('Action' != substr($method, -6)) {
throw new \InvalidArgumentException(sprintf('The "%s::%s" method does not look like a controller action (it does not end with Action)', $class, $method));
}
$action = substr($method, 0, -6);
if (!preg_match('/Controller\\\(.*)Controller$/', $class, $match)) {
throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it does not end with Controller)', $class));
}
$controller = $match[1];
$bundle = null;
$namespace = substr($class, 0, strrpos($class, '\\'));
foreach ($this->namespaces as $prefix) {
if (0 === $pos = strpos($namespace, $prefix)) {
// -11 to remove the \Controller suffix (11 characters)
$bundle = substr($namespace, strlen($prefix) + 1, -11);
}
}
if (null === $bundle) {
throw new \InvalidArgumentException(sprintf('The "%s" class does not belong to a known bundle namespace.', $class));
}
return str_replace('\\', '', $bundle).':'.$controller.':'.$action;
}
/**
* Converts a short notation a:b:c ro a class::method.
*
* @param string $controller A short notation controller (a:b:c)
*
* @param string A controler (class::method)
*/
public function fromShortNotation($controller)
{
if (3 != count($parts = explode(':', $controller))) {
throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid a:b:c controller string.', $controller));
}
list($bundle, $controller, $action) = $parts;
$class = null;
$logs = array();
foreach ($this->namespaces as $namespace) {
$prefix = null;
foreach ($this->kernel->getBundles() as $b) {
if ($bundle === $b->getName() && 0 === strpos($b->getNamespace(), $namespace)) {
$prefix = $b->getNamespace();
break;
}
}
if (null === $prefix) {
continue;
}
$try = $prefix.'\\Controller\\'.$controller.'Controller';
if (!class_exists($try)) {
if (null !== $this->logger) {
$logs[] = sprintf('Failed finding controller "%s:%s" from namespace "%s" (%s)', $bundle, $controller, $namespace, $try);
}
} else {
if (!$this->kernel->isClassInActiveBundle($try)) {
throw new \LogicException(sprintf('To use the "%s" controller, you first need to enable the Bundle "%s" in your Kernel class.', $try, $namespace.'\\'.$bundle));
}
$class = $try;
break;
}
}
if (null === $class) {
if (null !== $this->logger) {
foreach ($logs as $log) {
$this->logger->info($log);
}
}
throw new \InvalidArgumentException(sprintf('Unable to find controller "%s:%s".', $bundle, $controller));
}
return $class.'::'.$action.'Action';
}
}

View File

@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
/**
* ControllerNameParser converts controller from the short notation a:b:c
* (BlogBundle:Post:index) to a fully-qualified class::method string
* (Bundle\BlogBundle\Controller\PostController::indexAction).
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ControllerNameParser
{
protected $kernel;
protected $logger;
/**
* Constructor.
*
* @param Kernel $kernel A Kernel instance
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(Kernel $kernel, LoggerInterface $logger = null)
{
$this->kernel = $kernel;
$this->logger = $logger;
}
/**
* Converts a short notation a:b:c to a class::method.
*
* @param string $controller A short notation controller (a:b:c)
*
* @param string A controler (class::method)
*/
public function parse($controller)
{
if (3 != count($parts = explode(':', $controller))) {
throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid a:b:c controller string.', $controller));
}
list($bundle, $controller, $action) = $parts;
$class = null;
$logs = array();
foreach ($this->kernel->getBundle($bundle, false) as $b) {
$try = $b->getNamespace().'\\Controller\\'.$controller.'Controller';
if (!class_exists($try)) {
if (null !== $this->logger) {
$logs[] = sprintf('Failed finding controller "%s:%s" from namespace "%s" (%s)', $bundle, $controller, $b->getNamespace(), $try);
}
} else {
$class = $try;
break;
}
}
if (null === $class) {
if (null !== $this->logger) {
foreach ($logs as $log) {
$this->logger->info($log);
}
}
throw new \InvalidArgumentException(sprintf('Unable to find controller "%s:%s".', $bundle, $controller));
}
return $class.'::'.$action.'Action';
}
}

View File

@ -17,7 +17,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameConverter;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
/**
@ -28,20 +28,20 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface;
class ControllerResolver extends BaseControllerResolver
{
protected $container;
protected $converter;
protected $parser;
protected $esiSupport;
/**
* Constructor.
*
* @param ContainerInterface $container A ContainerInterface instance
* @param ControllerNameConverter $converter A ControllerNameConverter instance
* @param LoggerInterface $logger A LoggerInterface instance
* @param ContainerInterface $container A ContainerInterface instance
* @param ControllerNameParser $parser A ControllerNameParser instance
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(ContainerInterface $container, ControllerNameConverter $converter, LoggerInterface $logger = null)
public function __construct(ContainerInterface $container, ControllerNameParser $parser, LoggerInterface $logger = null)
{
$this->container = $container;
$this->converter = $converter;
$this->parser = $parser;
parent::__construct($logger);
}
@ -59,7 +59,7 @@ class ControllerResolver extends BaseControllerResolver
$count = substr_count($controller, ':');
if (2 == $count) {
// controller in the a:b:c notation then
$controller = $this->converter->fromShortNotation($controller);
$controller = $this->parser->parse($controller);
} elseif (1 == $count) {
// controller in the service:method notation
list($service, $method) = explode(':', $controller);

View File

@ -153,7 +153,7 @@ class FrameworkExtension extends Extension
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface',
'Symfony\\Bundle\\FrameworkBundle\\RequestListener',
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameConverter',
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser',
'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver',
'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller',
@ -213,13 +213,6 @@ class FrameworkExtension extends Extension
$container->setParameter('templating.assets.base_urls', $config['assets_base_urls']);
}
// template paths
$dirs = array('%kernel.root_dir%/views/%%bundle%%/%%controller%%/%%name%%.%%renderer%%.%%format%%');
foreach ($container->getParameter('kernel.bundle_dirs') as $dir) {
$dirs[] = $dir.'/%%bundle%%/Resources/views/%%controller%%/%%name%%.%%renderer%%.%%format%%';
}
$container->setParameter('templating.loader.filesystem.path', $dirs);
// path for the filesystem loader
if (isset($config['path'])) {
$container->setParameter('templating.loader.filesystem.path', $config['path']);
@ -313,7 +306,7 @@ class FrameworkExtension extends Extension
if ($first) {
// translation directories
$dirs = array();
foreach (array_reverse($container->getParameter('kernel.bundles')) as $bundle) {
foreach ($container->getParameter('kernel.bundles') as $bundle) {
$reflection = new \ReflectionClass($bundle);
if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) {
$dirs[] = $dir;

View File

@ -76,4 +76,20 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new AddClassesToCachePass());
$container->addCompilerPass(new TranslatorPass());
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -7,6 +7,7 @@
<parameters>
<parameter key="router.class">Symfony\Component\Routing\Router</parameter>
<parameter key="routing.loader.class">Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader</parameter>
<parameter key="routing.file_locator.class">Symfony\Bundle\FrameworkBundle\Routing\FileLocator</parameter>
<parameter key="routing.resolver.class">Symfony\Component\Routing\Loader\LoaderResolver</parameter>
<parameter key="routing.loader.xml.class">Symfony\Component\Routing\Loader\XmlFileLoader</parameter>
<parameter key="routing.loader.yml.class">Symfony\Component\Routing\Loader\YamlFileLoader</parameter>
@ -22,19 +23,23 @@
<services>
<service id="routing.resolver" class="%routing.resolver.class%" public="false" />
<service id="routing.file_locator" class="%routing.file_locator.class%" public="false">
<argument type="service" id="kernel" />
</service>
<service id="routing.loader.xml" class="%routing.loader.xml.class%" public="false">
<tag name="routing.loader" />
<argument>%kernel.bundle_dirs%</argument>
<argument type="service" id="routing.file_locator" />
</service>
<service id="routing.loader.yml" class="%routing.loader.yml.class%" public="false">
<tag name="routing.loader" />
<argument>%kernel.bundle_dirs%</argument>
<argument type="service" id="routing.file_locator" />
</service>
<service id="routing.loader.php" class="%routing.loader.php.class%" public="false">
<tag name="routing.loader" />
<argument>%kernel.bundle_dirs%</argument>
<argument type="service" id="routing.file_locator" />
</service>
<service id="routing.loader.real" class="%routing.loader.class%">

View File

@ -7,7 +7,9 @@
<parameters>
<parameter key="templating.engine.delegating.class">Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine</parameter>
<parameter key="templating.engine.php.class">Symfony\Bundle\FrameworkBundle\Templating\PhpEngine</parameter>
<parameter key="templating.loader.filesystem.class">Symfony\Component\Templating\Loader\FilesystemLoader</parameter>
<parameter key="templating.name_parser.class">Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateNameParser</parameter>
<parameter key="templating.locator.class">Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator</parameter>
<parameter key="templating.loader.filesystem.class">Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader</parameter>
<parameter key="templating.loader.cache.class">Symfony\Component\Templating\Loader\CacheLoader</parameter>
<parameter key="templating.loader.chain.class">Symfony\Component\Templating\Loader\ChainLoader</parameter>
<parameter key="templating.helper.slots.class">Symfony\Component\Templating\Helper\SlotsHelper</parameter>
@ -22,7 +24,6 @@
<parameter key="templating.helper.form.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper</parameter>
<parameter key="templating.assets.version">null</parameter>
<parameter key="templating.assets.base_urls" type="collection"></parameter>
<parameter key="templating.name_parser.class">Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser</parameter>
<parameter key="templating.renderer.php.class">Symfony\Component\Templating\Renderer\PhpRenderer</parameter>
<parameter key="debug.file_link_format">null</parameter>
</parameters>
@ -39,10 +40,18 @@
<call method="setCharset"><argument>%kernel.charset%</argument></call>
</service>
<service id="templating.loader.filesystem" class="%templating.loader.filesystem.class%" public="false">
<service id="templating.name_parser" class="%templating.name_parser.class%">
<argument type="service" id="kernel" />
</service>
<service id="templating.locator" class="%templating.locator.class%" public="false">
<argument type="service" id="kernel" />
<argument type="service" id="templating.name_parser" />
<argument>%templating.loader.filesystem.path%</argument>
<call method="setDebugger"><argument type="service" id="templating.debugger" on-invalid="ignore" /></call>
<argument>%kernel.root_dir%</argument>
</service>
<service id="templating.loader.filesystem" class="%templating.loader.filesystem.class%" public="false">
<argument type="service" id="templating.locator" />
</service>
<service id="templating.loader.cache" class="%templating.loader.cache.class%" public="false">
@ -107,10 +116,6 @@
<argument type="service" id="templating" />
</service>
<service id="templating.name_parser" class="%templating.name_parser.class%">
<argument type="service" id="kernel" />
</service>
<service id="templating.loader" alias="templating.loader.filesystem" />
<service id="templating" alias="templating.engine.delegating" />

View File

@ -7,7 +7,7 @@
<parameters>
<parameter key="request_listener.class">Symfony\Bundle\FrameworkBundle\RequestListener</parameter>
<parameter key="controller_resolver.class">Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver</parameter>
<parameter key="controller_name_converter.class">Symfony\Bundle\FrameworkBundle\Controller\ControllerNameConverter</parameter>
<parameter key="controller_name_converter.class">Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser</parameter>
<parameter key="response_listener.class">Symfony\Component\HttpKernel\ResponseListener</parameter>
<parameter key="exception_listener.class">Symfony\Component\HttpKernel\Debug\ExceptionListener</parameter>
<parameter key="exception_listener.controller">Symfony\Bundle\FrameworkBundle\Controller\ExceptionController::showAction</parameter>

View File

@ -11,7 +11,7 @@
namespace Symfony\Bundle\FrameworkBundle\Routing;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameConverter;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Component\Routing\Loader\DelegatingLoader as BaseDelegatingLoader;
use Symfony\Component\Routing\Loader\LoaderResolverInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@ -26,19 +26,19 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
*/
class DelegatingLoader extends BaseDelegatingLoader
{
protected $converter;
protected $parser;
protected $logger;
/**
* Constructor.
*
* @param ControllerNameConverter $converter A ControllerNameConverter instance
* @param LoggerInterface $logger A LoggerInterface instance
* @param LoaderResolverInterface $resolver A LoaderResolverInterface instance
* @param ControllerNameParser $parser A ControllerNameParser instance
* @param LoggerInterface $logger A LoggerInterface instance
* @param LoaderResolverInterface $resolver A LoaderResolverInterface instance
*/
public function __construct(ControllerNameConverter $converter, LoggerInterface $logger = null, LoaderResolverInterface $resolver)
public function __construct(ControllerNameParser $parser, LoggerInterface $logger = null, LoaderResolverInterface $resolver)
{
$this->converter = $converter;
$this->parser = $parser;
$this->logger = $logger;
parent::__construct($resolver);
@ -59,7 +59,7 @@ class DelegatingLoader extends BaseDelegatingLoader
foreach ($collection->all() as $name => $route) {
if ($controller = $route->getDefault('_controller')) {
try {
$controller = $this->converter->fromShortNotation($controller);
$controller = $this->parser->parse($controller);
} catch (\Exception $e) {
// unable to optimize unknown notation
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Routing;
use Symfony\Component\Routing\Loader\FileLocator as BaseFileLocator;
use Symfony\Component\HttpKernel\Kernel;
/**
* FileLocator uses the Kernel to locate resources in bundles.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FileLocator extends BaseFileLocator
{
protected $kernel;
/**
* Constructor.
*
* @param Kernel $kernel A Kernel instance
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct(Kernel $kernel, array $paths = array())
{
$this->kernel = $kernel;
parent::__construct($paths);
}
/**
* {@inheritdoc}
*/
public function locate($file)
{
if ('@' === $file[0]) {
return $this->kernel->locateResource($file);
}
return parent::locate($file);
}
}

View File

@ -0,0 +1,67 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Templating\Loader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\Loader\LoaderInterface;
/**
* FilesystemLoader is a loader that read templates from the filesystem.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FilesystemLoader implements LoaderInterface
{
protected $locator;
/**
* Constructor.
*
* @param TemplateNameParserInterface $nameParser A TemplateNameParserInterface instance
* @param string $path An overload path
*/
public function __construct(TemplateLocator $locator)
{
$this->locator = $locator;
}
/**
* Loads a template.
*
* @param string $template The logical template name
*
* @return Storage|Boolean false if the template cannot be loaded, a Storage instance otherwise
*/
public function load($template)
{
if (false === $file = $this->locator->locate($template)) {
return false;
}
return new FileStorage($file);
}
/**
* Returns true if the template is still fresh.
*
* @param string $template The template name
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($template, $time)
{
if (false === $template = $this->load($template)) {
return false;
}
return filemtime((string) $template) < $time;
}
}

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Templating\Loader;
use Symfony\Component\Templating\Loader\TemplateNameParserInterface;
use Symfony\Component\HttpKernel\Kernel;
/**
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TemplateLocator
{
protected $kernel;
protected $parser;
protected $path;
protected $cache;
/**
* Constructor.
*
* @param Kernel $kernel A Kernel instance
* @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance
* @param string $path A global fallback path
*/
public function __construct(Kernel $kernel, TemplateNameParserInterface $parser, $path)
{
$this->kernel = $kernel;
$this->path = $path;
$this->parser = $parser;
$this->cache = array();
}
public function locate($name)
{
// normalize name
$name = str_replace(':/' , ':', preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')));
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
if (false !== strpos($name, '..')) {
throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name));
}
$parameters = $this->parser->parse($name);
$resource = $parameters['bundle'].'/Resources/views/'.$parameters['controller'].'/'.$parameters['name'].'.'.$parameters['renderer'].'.'.$parameters['format'];
if (!$parameters['bundle']) {
if (is_file($file = $this->path.'/views/'.$parameters['controller'].'/'.$parameters['name'].'.'.$parameters['renderer'].'.'.$parameters['format'])) {
return $this->cache[$name] = $file;
}
throw new \InvalidArgumentException(sprintf('Unable to find template "%s" in "%s".', $name, $this->path));
}
try {
return $this->kernel->locateResource('@'.$resource, $this->path);
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('Unable to find template "%s".', $name, $this->path), 0, $e);
}
}
}

View File

@ -9,9 +9,9 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Templating;
namespace Symfony\Bundle\FrameworkBundle\Templating\Loader;
use Symfony\Component\Templating\TemplateNameParser as BaseTemplateNameParser;
use Symfony\Component\Templating\Loader\TemplateNameParser as BaseTemplateNameParser;
use Symfony\Component\HttpKernel\Kernel;
/**
@ -45,41 +45,26 @@ class TemplateNameParser extends BaseTemplateNameParser
throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.renderer.format").', $name));
}
$bundle = null;
if ($parts[0]) {
foreach ($this->kernel->getBundles() as $b) {
if ($parts[0] !== $b->getName()) {
continue;
}
foreach (array_keys($this->kernel->getBundleDirs()) as $prefix) {
if (0 === $pos = strpos($b->getNamespace(), $prefix)) {
$bundle = str_replace($prefix.'\\', '', $b->getNamespace());
break 2;
}
}
}
if (null === $bundle) {
throw new \InvalidArgumentException(sprintf('Unable to find a valid bundle name for template "%s".', $name));
}
}
$elements = explode('.', $parts[2]);
if (3 !== count($elements)) {
throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.renderer.format").', $name));
}
$parameters = array(
// bundle is used as part of the template path, so we need /
'bundle' => str_replace('\\', '/', $bundle),
'bundle' => $parts[0],
'controller' => $parts[1],
'name' => $elements[0],
'renderer' => $elements[1],
'format' => $elements[2],
);
if ($parameters['bundle']) {
try {
$this->kernel->getBundle($parameters['bundle']);
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid.', $name), 0, $e);
}
}
return $parameters;
}

View File

@ -1,88 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameConverter;
use Symfony\Bundle\FrameworkBundle\Tests\Logger;
use Symfony\Bundle\FrameworkBundle\Tests\Kernel;
require_once __DIR__.'/../Kernel.php';
require_once __DIR__.'/../Logger.php';
class ControllerNameConverterTest extends TestCase
{
public function testToShortNotation()
{
$kernel = new Kernel();
$kernel->boot();
$converter = new ControllerNameConverter($kernel);
$this->assertEquals('FooBundle:Default:index', $converter->toShortNotation('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->toShortNotation() converts a class::method string to the short a:b:c notation');
$this->assertEquals('SensioFooBundle:Default:index', $converter->toShortNotation('TestBundle\Sensio\FooBundle\Controller\DefaultController::indexAction'), '->toShortNotation() converts a class::method string to the short a:b:c notation');
$this->assertEquals('SensioCmsFooBundle:Default:index', $converter->toShortNotation('TestBundle\Sensio\Cms\FooBundle\Controller\DefaultController::indexAction'), '->toShortNotation() converts a class::method string to the short a:b:c notation');
try {
$converter->toShortNotation('foo');
$this->fail('->toShortNotation() throws an \InvalidArgumentException if the controller is not a class::method string');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->toShortNotation() throws an \InvalidArgumentException if the controller is not a class::method string');
}
try {
$converter->toShortNotation('Symfony\Bundle\FooBundle\Controller\FooController::bar');
$this->fail('->toShortNotation() throws an \InvalidArgumentException if the method does not end with Action');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->toShortNotation() throws an \InvalidArgumentException if the method does not end with Action');
}
try {
$converter->toShortNotation('Symfony\Bundle\FooBundle\FooController::barAction');
$this->fail('->toShortNotation() throws an \InvalidArgumentException if the class does not end with Controller');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->toShortNotation() throws an \InvalidArgumentException if the class does not end with Controller');
}
try {
$converter->toShortNotation('FooBar\Bundle\FooBundle\Controller\FooController::barAction');
$this->fail('->toShortNotation() throws an \InvalidArgumentException if the class does not belongs to a known namespace');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->toShortNotation() throws an \InvalidArgumentException if the class does not belongs to a known namespace');
}
}
public function testFromShortNotation()
{
$kernel = new Kernel();
$kernel->boot();
$logger = new Logger();
$converter = new ControllerNameConverter($kernel, $logger);
$this->assertEquals('TestBundle\FooBundle\Controller\DefaultController::indexAction', $converter->fromShortNotation('FooBundle:Default:index'), '->fromShortNotation() converts a short a:b:c notation string to a class::method string');
$this->assertEquals('TestApplication\Sensio\FooBundle\Controller\DefaultController::indexAction', $converter->fromShortNotation('SensioFooBundle:Default:index'), '->fromShortNotation() converts a short a:b:c notation string to a class::method string');
$this->assertEquals('TestBundle\Sensio\Cms\FooBundle\Controller\DefaultController::indexAction', $converter->fromShortNotation('SensioCmsFooBundle:Default:index'), '->fromShortNotation() converts a short a:b:c notation string to a class::method string');
try {
$converter->fromShortNotation('foo:');
$this->fail('->fromShortNotation() throws an \InvalidArgumentException if the controller is not an a:b:c string');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->fromShortNotation() throws an \InvalidArgumentException if the controller is not an a:b:c string');
}
try {
$converter->fromShortNotation('BarBundle:Default:index');
$this->fail('->fromShortNotation() throws a \InvalidArgumentException if the class is found but does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->fromShortNotation() throws a \LogicException if the class is found but does not exist');
}
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Bundle\FrameworkBundle\Tests\Logger;
use Symfony\Bundle\FrameworkBundle\Tests\Kernel;
require_once __DIR__.'/../Kernel.php';
require_once __DIR__.'/../Logger.php';
class ControllerNameParserTest extends TestCase
{
public function testParse()
{
$kernel = new Kernel();
$kernel->boot();
$logger = new Logger();
$parser = new ControllerNameParser($kernel, $logger);
$this->assertEquals('TestBundle\FooBundle\Controller\DefaultController::indexAction', $parser->parse('FooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string');
$this->assertEquals('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction', $parser->parse('FooBundle:Sub\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string');
$this->assertEquals('TestBundle\Fabpot\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string');
$this->assertEquals('TestBundle\Sensio\Cms\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioCmsFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string');
try {
$parser->parse('foo:');
$this->fail('->parse() throws an \InvalidArgumentException if the controller is not an a:b:c string');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an a:b:c string');
}
try {
$parser->parse('BarBundle:Default:index');
$this->fail('->parse() throws a \InvalidArgumentException if the class is found but does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws a \LogicException if the class is found but does not exist');
}
}
}

View File

@ -49,12 +49,7 @@ class FrameworkExtensionTest extends TestCase
protected function getContainer()
{
return new ContainerBuilder(new ParameterBag(array(
'kernel.bundle_dirs' => array(
'Symfony\\Framework' => __DIR__ . '/../../../Framework',
),
'kernel.bundles' => array(
'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle',
),
'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'),
'kernel.root_dir' => __DIR__,
'kernel.debug' => false,
'kernel.compiled_classes' => array(),

View File

@ -1,6 +1,6 @@
<?php
namespace TestApplication\Sensio\FooBundle\Controller;
namespace TestBundle\Fabpot\FooBundle\Controller;
/*
* This file is part of the Symfony framework.

View File

@ -0,0 +1,46 @@
<?php
namespace TestBundle\Fabpot\FooBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/*
* 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.
*/
/**
* Bundle.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FabpotFooBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'SensioFooBundle';
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -1,8 +1,6 @@
<?php
namespace TestApplication\Sensio\FooBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
namespace TestBundle\FooBundle\Controller\Sub;
/*
* This file is part of the Symfony framework.
@ -14,10 +12,10 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
/**
* Bundle.
* DefaultController.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class SensioFooBundle extends Bundle
class DefaultController
{
}

View File

@ -20,4 +20,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class FooBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -20,4 +20,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class SensioCmsFooBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -20,4 +20,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class SensioFooBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -54,21 +54,10 @@ class Kernel extends BaseKernel
{
return array(
new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new \TestBundle\FooBundle\FooBundle(),
new \TestBundle\Sensio\FooBundle\SensioFooBundle(),
new \TestApplication\Sensio\FooBundle\SensioFooBundle(),
new \TestBundle\Sensio\Cms\FooBundle\SensioCmsFooBundle(),
);
}
public function registerBundleDirs()
{
return array(
'Application' => __DIR__.'/../src/Application',
'Bundle' => __DIR__.'/../src/Bundle',
'TestApplication' => __DIR__.'/Fixtures/TestApplication',
'TestBundle' => __DIR__.'/Fixtures/TestBundle',
'Symfony\\Bundle' => __DIR__.'/../src/vendor/symfony/src/Symfony/Bundle',
new \TestBundle\FooBundle\FooBundle(),
new \TestBundle\Fabpot\FooBundle\FabpotFooBundle(),
);
}
@ -85,9 +74,10 @@ class Kernel extends BaseKernel
throw new \LogicException('The kernel is already booted.');
}
$this->bundles = $this->registerBundles();
$this->bundleDirs = $this->registerBundleDirs();
// init bundles
$this->initializeBundles();
// init container
$this->initializeContainer();
foreach ($this->bundles as $bundle) {

View File

@ -9,10 +9,10 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Templating;
namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Loader;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser;
use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateNameParser;
use Symfony\Bundle\FrameworkBundle\Tests\Kernel;
class TemplateNameParserTest extends TestCase
@ -24,9 +24,9 @@ class TemplateNameParserTest extends TestCase
{
$kernel = new Kernel();
$kernel->boot();
$converter = new TemplateNameParser($kernel);
$parser = new TemplateNameParser($kernel);
$this->assertEquals($parameters, $converter->parse($name));
$this->assertEquals($parameters, $parser->parse($name));
}
public function getParseTests()
@ -35,8 +35,8 @@ class TemplateNameParserTest extends TestCase
array('FooBundle:Post:index.php.html', array('name' => 'index', 'bundle' => 'FooBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'html')),
array('FooBundle:Post:index.twig.html', array('name' => 'index', 'bundle' => 'FooBundle', 'controller' => 'Post', 'renderer' => 'twig', 'format' => 'html')),
array('FooBundle:Post:index.php.xml', array('name' => 'index', 'bundle' => 'FooBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'xml')),
array('SensioFooBundle:Post:index.php.html', array('name' => 'index', 'bundle' => 'Sensio/FooBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'html')),
array('SensioCmsFooBundle:Post:index.php.html', array('name' => 'index', 'bundle' => 'Sensio/Cms/FooBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'html')),
array('SensioFooBundle:Post:index.php.html', array('name' => 'index', 'bundle' => 'SensioFooBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'html')),
array('SensioCmsFooBundle:Post:index.php.html', array('name' => 'index', 'bundle' => 'SensioCmsFooBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'html')),
array(':Post:index.php.html',array('name' => 'index', 'bundle' => '', 'controller' => 'Post', 'renderer' => 'php', 'format' => 'html')),
array('::index.php.html', array('name' => 'index', 'bundle' => '', 'controller' => '', 'renderer' => 'php', 'format' => 'html')),
);
@ -50,9 +50,9 @@ class TemplateNameParserTest extends TestCase
{
$kernel = new Kernel();
$kernel->boot();
$converter = new TemplateNameParser($kernel);
$parser = new TemplateNameParser($kernel);
$converter->parse($name);
$parser->parse($name);
}
public function getParseInvalidTests()

View File

@ -20,4 +20,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class SwiftmailerBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -43,7 +43,6 @@ class TwigExtension extends Extension
'Twig_Extension_Escaper',
'Twig_Extension_Optimizer',
'Twig_LoaderInterface',
'Twig_Loader_Filesystem',
'Twig_Markup',
'Twig_TemplateInterface',
'Twig_Template',

View File

@ -11,8 +11,7 @@
namespace Symfony\Bundle\TwigBundle\Loader;
use Symfony\Component\Templating\TemplateNameParserInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator;
/**
* FilesystemLoader extends the default Twig filesystem loader
@ -20,78 +19,62 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FilesystemLoader extends \Twig_Loader_Filesystem
class FilesystemLoader implements \Twig_LoaderInterface
{
protected $nameParser;
protected $logger;
/**
* Constructor.
*
* @param Kernel $kernel A Kernel instance
* @param TemplateNameParserInterface $nameParser A TemplateNameParserInterface instance
* @param string $path A global fallback path
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(TemplateNameParserInterface $nameParser, array $paths = array(), LoggerInterface $logger = null)
public function __construct(TemplateLocator $locator)
{
parent::__construct($paths);
$this->nameParser = $nameParser;
$this->logger = $logger;
$this->locator = $locator;
}
/**
* {@inheritdoc}
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
*/
public function setPaths($paths)
public function getSource($name)
{
// invalidate the cache
$this->cache = array();
return file_get_contents($this->findTemplate($name));
}
// we don't check if the directory exists here as we have path patterns, not paths
$this->paths = is_array($paths) ? $paths : array($paths);
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
*/
public function getCacheKey($name)
{
return $this->findTemplate($name);
}
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($name, $time)
{
return filemtime($this->findTemplate($name)) < $time;
}
protected function findTemplate($name)
{
// normalize name
$name = str_replace(':/' , ':', preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')));
if (isset($this->cache[$name])) {
return $this->cache[$name];
if (false === $file = $this->locator->locate($name)) {
throw new \Twig_Error_Loader(sprintf('Unable to find template "%s".', $name));
}
$parameters = $this->nameParser->parse($name);
$this->validateName($parameters['name']);
$this->validateName($parameters['bundle']);
$this->validateName($parameters['controller']);
$this->validateName($parameters['format']);
$replacements = array();
foreach ($parameters as $key => $value) {
$replacements['%'.$key.'%'] = $value;
}
$logs = array();
foreach ($this->paths as $path) {
if (is_file($file = strtr($path, $replacements))) {
if (null !== $this->logger) {
$this->logger->info(sprintf('Loaded template file "%s"', $file));
}
return $this->cache[$name] = $file;
}
if (null !== $this->logger) {
$logs[] = sprintf('Failed loading template file "%s"', $file);
}
}
if (null !== $this->logger) {
foreach ($logs as $log) {
$this->logger->debug($log);
}
}
throw new \Twig_Error_Loader(sprintf('Unable to find template "%s".', $name));
return $file;
}
}

View File

@ -26,9 +26,7 @@
</service>
<service id="twig.loader" class="%twig.loader.class%">
<argument type="service" id="templating.name_parser" />
<argument>%templating.loader.filesystem.path%</argument>
<argument type="service" id="logger" on-invalid="ignore" />
<argument type="service" id="templating.locator" />
</service>
<service id="templating.engine.twig" class="%templating.engine.twig.class%">

View File

@ -28,4 +28,20 @@ class TwigBundle extends Bundle
$container->addCompilerPass(new TwigEnvironmentPass());
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -20,4 +20,19 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
*/
class WebProfilerBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -28,4 +28,20 @@ class ZendBundle extends Bundle
$container->addCompilerPass(new ZendLoggerWriterPass());
}
/**
* {@inheritdoc}
*/
public function getNamespace()
{
return __NAMESPACE__;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return strtr(__DIR__, '\\', '/');
}
}

View File

@ -18,18 +18,13 @@ use Symfony\Component\Console\Application;
use Symfony\Component\Finder\Finder;
/**
* An implementation of the BundleInterface that follows a few conventions
* for the DependencyInjection extensions and the Console commands.
* An implementation of BundleInterface that adds a few conventions
* for DependencyInjection extensions and Console commands.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class Bundle extends ContainerAware implements BundleInterface
{
protected $name;
protected $namespace;
protected $path;
protected $reflection;
/**
* Boots the Bundle.
*/
@ -45,59 +40,13 @@ abstract class Bundle extends ContainerAware implements BundleInterface
}
/**
* Gets the Bundle name.
* Returns the bundle parent name.
*
* @return string The Bundle name
* @return string The Bundle parent name it overrides or null if no parent
*/
public function getName()
public function getParent()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->name;
}
/**
* Gets the Bundle namespace.
*
* @return string The Bundle namespace
*/
public function getNamespace()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->namespace;
}
/**
* Gets the Bundle absolute path.
*
* @return string The Bundle absolute path
*/
public function getPath()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->path;
}
/**
* Gets the Bundle Reflection instance.
*
* @return \ReflectionObject A \ReflectionObject instance for the Bundle
*/
public function getReflection()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->reflection;
return null;
}
/**
@ -119,7 +68,7 @@ abstract class Bundle extends ContainerAware implements BundleInterface
$finder = new Finder();
$finder->files()->name('*Extension.php')->in($dir);
$prefix = $this->namespace.'\\DependencyInjection';
$prefix = $this->getNamespace().'\\DependencyInjection';
foreach ($finder as $file) {
$class = $prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.$file->getBasename('.php');
@ -146,7 +95,7 @@ abstract class Bundle extends ContainerAware implements BundleInterface
$finder = new Finder();
$finder->files()->name('*Command.php')->in($dir);
$prefix = $this->namespace.'\\Command';
$prefix = $this->getNamespace().'\\Command';
foreach ($finder as $file) {
$r = new \ReflectionClass($prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.$file->getBasename('.php'));
if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract()) {
@ -154,16 +103,4 @@ abstract class Bundle extends ContainerAware implements BundleInterface
}
}
}
/**
* Initializes the properties on this object that require a reflection
* object to have been created.
*/
protected function initReflection()
{
$this->reflection = new \ReflectionObject($this);
$this->namespace = $this->reflection->getNamespaceName();
$this->name = $this->reflection->getShortName();
$this->path = str_replace('\\', '/', dirname($this->reflection->getFilename()));
}
}

View File

@ -27,4 +27,27 @@ interface BundleInterface
* Shutdowns the Bundle.
*/
function shutdown();
/**
* Returns the bundle parent class.
*
* @return string The Bundle parent class name it overrides or null if no parent
*/
function getParent();
/**
* Gets the Bundle namespace.
*
* @return string The Bundle namespace
*/
function getNamespace();
/**
* Gets the Bundle directory path.
*
* The path should always be returned as a Unix path (with /).
*
* @return string The Bundle absolute path
*/
function getPath();
}

View File

@ -26,6 +26,7 @@ use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\ClassCollectionLoader;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
/**
* The Kernel is the heart of the Symfony system. It manages an environment
@ -36,7 +37,7 @@ use Symfony\Component\HttpKernel\ClassCollectionLoader;
abstract class Kernel implements HttpKernelInterface, \Serializable
{
protected $bundles;
protected $bundleDirs;
protected $bundleMap;
protected $container;
protected $rootDir;
protected $environment;
@ -97,16 +98,6 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
*/
abstract public function registerBundles();
/**
* Returns the directories where bundles can be found.
*
* The order is significant and can be used by the application.
* For instance, it can be used to override templates.
*
* @return array A hash with namespaces as keys and directories as values
*/
abstract public function registerBundleDirs();
/**
* Loads the container configuration
*
@ -140,9 +131,10 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
require_once __DIR__.'/bootstrap.php';
$this->bundles = $this->registerBundles();
$this->bundleDirs = $this->registerBundleDirs();
// init bundles
$this->initializeBundles();
// init container
$this->initializeContainer();
// load core classes
@ -213,19 +205,9 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
}
/**
* Gets the directories where bundles can be stored.
* Gets the registered bundle instances.
*
* @return array An array of directories where bundles can be stored
*/
public function getBundleDirs()
{
return $this->bundleDirs;
}
/**
* Gets the registered bundle names.
*
* @return array An array of registered bundle names
* @return array An array of registered bundle instances
*/
public function getBundles()
{
@ -251,6 +233,98 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
return false;
}
/**
* Returns a bundle by its name.
*
* @param string $name Bundle name
* @param Boolean $first Whether to return the first bundle or all bundles matching this name
*
* @return BundleInterface A BundleInterface instance
*
* @throws \InvalidArgumentException when the bundle is not enabled
*/
public function getBundle($name, $first = true)
{
if (!isset($this->bundleMap[$name])) {
throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.', $name));
}
if (true === $first) {
return $this->bundleMap[$name][0];
} elseif (false === $first) {
return $this->bundleMap[$name];
}
}
/**
* Returns the file path for a given resource.
*
* The resource name must follow the following pattern:
*
* @BundleName/path/to/a/file.something
*
* where BundleName is the name of the bundle
* and the remaining part is the relative path in the bundle.
*
* If $dir is passed, and the first segment of the path is Resources,
* this method will look for a file named:
*
* $dir/BundleName/path/without/Resources
*
* @param string $name A resource name to locate
* @param string $dir A directory where to look for the resource first
* @param Boolean $first Whether to return the first path or paths for all matching bundles
*
* @return string|array The absolute path of the resource or an array if $first is false
*
* @throws \InvalidArgumentException if the file cannot be found or the name is not valid
* @throws \RuntimeException if the name contains invalid/unsafe characters
*/
public function locateResource($name, $dir = null, $first = true)
{
if ('@' !== $name[0]) {
throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name));
}
if (false !== strpos($name, '..')) {
throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name));
}
$name = substr($name, 1);
list($bundle, $path) = explode('/', $name, 2);
$isResource = 0 === strpos($path, 'Resources');
// return the first matching one
if (true === $first) {
if (true === $isResource && null !== $dir && is_file($file = $dir.'/'.$bundle.'/'.substr($path, 10))) {
return $file;
} elseif (is_file($file = $this->getBundle($bundle)->getPath().'/'.$path)) {
return $file;
}
throw new \InvalidArgumentException(sprintf('Unable to find file "@%s".', $name));
}
// return them all
$files = array();
if (true === $isResource && null !== $dir && is_file($file = $dir.'/'.$bundle.'/'.substr($path, 10))) {
$files[] = $file;
}
foreach ($this->getBundle($bundle, false) as $bundle) {
if (is_file($file = $bundle->getPath().'/'.$path)) {
$files[] = $file;
}
}
if ($files) {
return $files;
}
throw new \InvalidArgumentException(sprintf('Unable to find file "@%s".', $name));
}
public function getName()
{
return $this->name;
@ -331,6 +405,42 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
return $this->rootDir.'/logs';
}
protected function initializeBundles()
{
// init bundles
$this->bundles = array();
foreach ($this->registerBundles() as $bundle) {
$parts = explode('\\', get_class($bundle));
$name = $parts[count($parts) - 1];
$this->bundles[$name] = $bundle;
if (!isset($this->bundleMap[$name])) {
$this->bundleMap[$name] = array();
}
$this->bundleMap[$name][] = $bundle;
}
// inheritance
$extended = array();
foreach ($this->bundles as $name => $bundle) {
$parent = $bundle;
$first = true;
while ($parentName = $parent->getParent()) {
if (!isset($this->bundles[$parentName])) {
throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $name, $parentName));
}
if ($first && isset($extended[$parentName])) {
throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $extended[$parentName]));
}
$first = false;
$parent = $this->bundles[$parentName];
$extended[$parentName] = $name;
array_unshift($this->bundleMap[$parentName], $bundle);
}
}
}
protected function initializeContainer()
{
$class = $this->getSafeName().ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer';
@ -351,21 +461,20 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
public function getKernelParameters()
{
$bundles = array();
foreach ($this->bundles as $bundle) {
$bundles[] = get_class($bundle);
foreach ($this->bundles as $name => $bundle) {
$bundles[$name] = get_class($bundle);
}
return array_merge(
array(
'kernel.root_dir' => $this->rootDir,
'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug,
'kernel.name' => $this->name,
'kernel.cache_dir' => $this->getCacheDir(),
'kernel.logs_dir' => $this->getLogDir(),
'kernel.bundle_dirs' => $this->bundleDirs,
'kernel.bundles' => $bundles,
'kernel.charset' => 'UTF-8',
'kernel.root_dir' => $this->rootDir,
'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug,
'kernel.name' => $this->name,
'kernel.cache_dir' => $this->getCacheDir(),
'kernel.logs_dir' => $this->getLogDir(),
'kernel.bundles' => $bundles,
'kernel.charset' => 'UTF-8',
),
$this->getEnvParameters()
);
@ -453,10 +562,10 @@ abstract class Kernel implements HttpKernelInterface, \Serializable
protected function getContainerLoader(ContainerInterface $container)
{
$resolver = new LoaderResolver(array(
new XmlFileLoader($container, $this->getBundleDirs()),
new YamlFileLoader($container, $this->getBundleDirs()),
new IniFileLoader($container, $this->getBundleDirs()),
new PhpFileLoader($container, $this->getBundleDirs()),
new XmlFileLoader($container),
new YamlFileLoader($container),
new IniFileLoader($container),
new PhpFileLoader($container),
new ClosureLoader($container),
));

View File

@ -8,43 +8,15 @@ use Symfony\Component\Console\Application;
use Symfony\Component\Finder\Finder;
abstract class Bundle extends ContainerAware implements BundleInterface
{
protected $name;
protected $namespace;
protected $path;
protected $reflection;
public function boot()
{
}
public function shutdown()
{
}
public function getName()
public function getParent()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->name;
}
public function getNamespace()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->namespace;
}
public function getPath()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->path;
}
public function getReflection()
{
if (null === $this->name) {
$this->initReflection();
}
return $this->reflection;
return null;
}
public function registerExtensions(ContainerBuilder $container)
{
@ -53,7 +25,7 @@ abstract class Bundle extends ContainerAware implements BundleInterface
}
$finder = new Finder();
$finder->files()->name('*Extension.php')->in($dir);
$prefix = $this->namespace.'\\DependencyInjection';
$prefix = $this->getNamespace().'\\DependencyInjection';
foreach ($finder as $file) {
$class = $prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.$file->getBasename('.php');
$container->registerExtension(new $class());
@ -66,7 +38,7 @@ abstract class Bundle extends ContainerAware implements BundleInterface
}
$finder = new Finder();
$finder->files()->name('*Command.php')->in($dir);
$prefix = $this->namespace.'\\Command';
$prefix = $this->getNamespace().'\\Command';
foreach ($finder as $file) {
$r = new \ReflectionClass($prefix.strtr($file->getPath(), array($dir => '', '/' => '\\')).'\\'.$file->getBasename('.php'));
if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract()) {
@ -74,13 +46,6 @@ abstract class Bundle extends ContainerAware implements BundleInterface
}
}
}
protected function initReflection()
{
$this->reflection = new \ReflectionObject($this);
$this->namespace = $this->reflection->getNamespaceName();
$this->name = $this->reflection->getShortName();
$this->path = str_replace('\\', '/', dirname($this->reflection->getFilename()));
}
}
}
namespace Symfony\Component\HttpKernel\Bundle
@ -89,6 +54,9 @@ interface BundleInterface
{
function boot();
function shutdown();
function getParent();
function getNamespace();
function getPath();
}
}
namespace Symfony\Component\HttpKernel\Debug

View File

@ -33,7 +33,7 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader
*/
public function load($path, $type = null)
{
$dir = $this->getAbsolutePath($path);
$dir = $this->locator->getAbsolutePath($path);
if (!file_exists($dir)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist (in: %s).', $dir, implode(', ', $this->paths)));
}
@ -62,6 +62,6 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader
*/
public function supports($resource, $type = null)
{
return is_string($resource) && is_dir($this->getAbsolutePath($resource)) && (!$type || 'annotation' === $type);
return is_string($resource) && is_dir($this->locator->getAbsolutePath($resource)) && (!$type || 'annotation' === $type);
}
}

View File

@ -30,13 +30,13 @@ class AnnotationFileLoader extends FileLoader
* @param AnnotationClassLoader $loader An AnnotationClassLoader instance
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct(AnnotationClassLoader $loader, $paths = array())
public function __construct(FileLocator $locator, AnnotationClassLoader $loader, $paths = array())
{
if (!function_exists('token_get_all')) {
throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
}
parent::__construct($paths);
parent::__construct($locator, $paths);
$this->loader = $loader;
}
@ -53,7 +53,7 @@ class AnnotationFileLoader extends FileLoader
*/
public function load($file, $type = null)
{
$path = $this->getAbsolutePath($file);
$path = $this->locator->getAbsolutePath($file);
if (!file_exists($path)) {
throw new \InvalidArgumentException(sprintf('The file "%s" cannot be found (in: %s).', $file, implode(', ', $this->paths)));
}

View File

@ -34,7 +34,7 @@ class AnnotationGlobLoader extends AnnotationDirectoryLoader
public function load($glob, $type = null)
{
$collection = new RouteCollection();
foreach ($this->getAbsolutePaths($glob) as $path) {
foreach ($this->locator->getAbsolutePaths($glob) as $path) {
$collection->addCollection(parent::load($path, $type));
}

View File

@ -20,20 +20,17 @@ use Symfony\Component\Routing\RouteCollection;
*/
abstract class FileLoader extends Loader
{
protected $locator;
protected $currentDir;
protected $paths;
/**
* Constructor.
*
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct($paths = array())
public function __construct(FileLocator $locator)
{
if (!is_array($paths)) {
$paths = array($paths);
}
$this->paths = $paths;
$this->locator = $locator;
}
public function getLocator()
{
return $this->locator;
}
/**
@ -49,74 +46,9 @@ abstract class FileLoader extends Loader
$loader = $this->resolve($resource, $type);
if ($loader instanceof FileLoader && null !== $this->currentDir) {
$resource = $this->getAbsolutePath($resource, $this->currentDir);
$resource = $this->locator->getAbsolutePath($resource, $this->currentDir);
}
return $loader->load($resource, $type);
}
/**
* Returns a full path for a given file.
*
* @param string $file A file path
*
* @return string The full path for the file
*
* @throws \InvalidArgumentException When file is not found
*/
protected function findFile($file)
{
$path = $this->getAbsolutePath($file);
if (!file_exists($path)) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $file, implode(', ', $this->paths)));
}
return $path;
}
/**
* Gets the absolute path for the file path if possible.
*
* @param string $file A file path
* @param string $currentPath The current path
*
* @return string
*/
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;
}
/**
* Returns whether the file path is an absolute path.
*
* @param string $file A file path
*
* @return boolean
*/
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;
}
}

View File

@ -0,0 +1,100 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Routing\Loader;
/**
* FileLocator uses an array of pre-defined paths to find files.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FileLocator
{
protected $paths;
/**
* Constructor.
*
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct($paths = array())
{
if (!is_array($paths)) {
$paths = array($paths);
}
$this->paths = $paths;
}
/**
* Returns a full path for a given file.
*
* @param string $file A file path
*
* @return string The full path for the file
*
* @throws \InvalidArgumentException When file is not found
*/
public function locate($file)
{
$path = $this->getAbsolutePath($file);
if (!file_exists($path)) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $file, implode(', ', $this->paths)));
}
return $path;
}
/**
* Gets the absolute path for the file path if possible.
*
* @param string $file A file path
* @param string $currentPath The current path
*
* @return string
*/
public function getAbsolutePath($file, $currentPath = null)
{
if ($this->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;
}
/**
* Returns whether the file path is an absolute path.
*
* @param string $file A file path
*
* @return boolean
*/
public 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;
}
}

View File

@ -32,7 +32,7 @@ class PhpFileLoader extends FileLoader
{
$loader = $this;
$path = $this->findFile($file);
$path = $this->locator->locate($file);
$collection = include $path;
$this->currentDir = dirname($path);

View File

@ -34,7 +34,7 @@ class XmlFileLoader extends FileLoader
*/
public function load($file, $type = null)
{
$path = $this->findFile($file);
$path = $this->locator->locate($file);
$xml = $this->loadFile($path);
@ -56,7 +56,8 @@ class XmlFileLoader extends FileLoader
$type = (string) $node->getAttribute('type');
$prefix = (string) $node->getAttribute('prefix');
$this->currentDir = dirname($path);
$collection->addCollection($this->import($resource, $type), $prefix);
$file = $this->locator->locate($resource);
$collection->addCollection($this->import($file, $type), $prefix);
break;
default:
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));

View File

@ -35,7 +35,7 @@ class YamlFileLoader extends FileLoader
*/
public function load($file, $type = null)
{
$path = $this->findFile($file);
$path = $this->locator->locate($file);
$config = $this->loadFile($path);
@ -57,7 +57,8 @@ class YamlFileLoader extends FileLoader
$type = isset($config['type']) ? $config['type'] : null;
$prefix = isset($config['prefix']) ? $config['prefix'] : null;
$this->currentDir = dirname($path);
$collection->addCollection($this->import($config['resource'], $type), $prefix);
$file = $this->locator->locate($config['resource']);
$collection->addCollection($this->import($file, $type), $prefix);
} elseif (isset($config['pattern'])) {
$this->parseRoute($collection, $name, $config, $path);
} else {

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\TemplateNameParserInterface;
/**
* FilesystemLoader is a loader that read templates from the filesystem.

View File

@ -12,7 +12,6 @@
namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\DebuggerInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;
/**
* Loader is the base class for all template loader classes.

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
namespace Symfony\Component\Templating\Loader;
/**
* TemplateNameParser is the default implementation of TemplateNameParserInterface.

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
namespace Symfony\Component\Templating\Loader;
/**
* TemplateNameParserInterface parses template name to a template name and an array of options.

View File

@ -22,6 +22,218 @@ class KernelTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo', $kernel->getSafeName());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testLocateResourceThrowsExceptionWhenNameIsNotValid()
{
$this->getKernelForInvalidLocateResource()->locateResource('foo');
}
/**
* @expectedException \RuntimeException
*/
public function testLocateResourceThrowsExceptionWhenNameIsUnsafe()
{
$this->getKernelForInvalidLocateResource()->locateResource('@foo/../bar');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testLocateResourceThrowsExceptionWhenBundleDoesNotExist()
{
$this->getKernelForInvalidLocateResource()->locateResource('@foo/config/routing.xml');
}
public function testLocateResourceReturnsTheFirstThatMatches()
{
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('getBundle')
->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1')))
;
$this->assertEquals(__DIR__.'/Fixtures/Bundle1/foo.txt', $kernel->locateResource('@foo/foo.txt'));
}
public function testLocateResourceReturnsTheAllMatches()
{
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('getBundle')
->with($this->anything(), $this->equalTo(false))
->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1'), $this->getBundle(__DIR__.'/Fixtures/Bundle2'))))
;
$this->assertEquals(array(__DIR__.'/Fixtures/Bundle1/foo.txt', __DIR__.'/Fixtures/Bundle2/foo.txt'), $kernel->locateResource('@foo/foo.txt', null, false));
}
public function testLocateResourceReturnsAllMatchesBis()
{
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('getBundle')
->with($this->anything(), $this->equalTo(false))
->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1'), $this->getBundle(__DIR__.'/foobar'))))
;
$this->assertEquals(array(__DIR__.'/Fixtures/Bundle1/foo.txt'), $kernel->locateResource('@foo/foo.txt', null, false));
}
public function testLocateResourceIgnoresDirOnNonResource()
{
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('getBundle')
->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1')))
;
$this->assertEquals(__DIR__.'/Fixtures/Bundle1/foo.txt', $kernel->locateResource('@foo/foo.txt', __DIR__.'/Fixtures'));
}
public function testLocateResourceReturnsTheDirOneForResources()
{
$kernel = $this->getKernel();
$this->assertEquals(__DIR__.'/Fixtures/foo/foo.txt', $kernel->locateResource('@foo/Resources/foo.txt', __DIR__.'/Fixtures'));
}
public function testLocateResourceReturnsTheDirOneForResourcesAndBundleOnes()
{
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('getBundle')
->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1'))))
;
$this->assertEquals(array(__DIR__.'/Fixtures/foo/foo.txt', __DIR__.'/Fixtures/Bundle1/Resources/foo.txt'), $kernel->locateResource('@foo/Resources/foo.txt', __DIR__.'/Fixtures', false));
}
public function testInitializeBundles()
{
$parent = $this->getBundle(null, '', 'ParentABundle');
$child = $this->getBundle(null, 'ParentABundle', 'ChildABundle');
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('registerBundles')
->will($this->returnValue(array($parent, $child)))
;
$kernel->initializeBundles();
$map = $kernel->getBundleMap();
$this->assertEquals(array($child, $parent), $map['ParentABundle']);
}
public function testInitializeBundlesSupportInheritanceCascade()
{
$grandparent = $this->getBundle(null, '', 'GrandParentBBundle');
$parent = $this->getBundle(null, 'GrandParentBBundle', 'ParentBBundle');
$child = $this->getBundle(null, 'ParentBBundle', 'ChildBBundle');
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('registerBundles')
->will($this->returnValue(array($parent, $grandparent, $child)))
;
$kernel->initializeBundles();
$map = $kernel->getBundleMap();
$this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentBBundle']);
$this->assertEquals(array($child, $parent), $map['ParentBBundle']);
$this->assertEquals(array($child), $map['ChildBBundle']);
}
/**
* @expectedException \LogicException
*/
public function testInitializeBundlesThrowsExceptionWhenAParentDoesNotExists()
{
$child = $this->getBundle(null, 'FooBar', 'ChildCBundle');
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('registerBundles')
->will($this->returnValue(array($child)))
;
$kernel->initializeBundles();
}
/**
* @expectedException \LogicException
*/
public function testInitializeBundlesThrowsExceptionWhenABundleIsDirectlyExtendedByTwoBundles()
{
$parent = $this->getBundle(null, '', 'ParentCBundle');
$child1 = $this->getBundle(null, 'ParentCBundle', 'ChildC1Bundle');
$child2 = $this->getBundle(null, 'ParentCBundle', 'ChildC2Bundle');
$kernel = $this->getKernel();
$kernel
->expects($this->once())
->method('registerBundles')
->will($this->returnValue(array($parent, $child1, $child2)))
;
$kernel->initializeBundles();
}
protected function getBundle($dir = null, $parent = null, $className = null)
{
$bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface');
if ($className) {
$bundle->setMockClassName($className);
}
$bundle = $bundle->getMock();
if (null !== $dir) {
$bundle
->expects($this->any())
->method('getPath')
->will($this->returnValue($dir))
;
}
if (null !== $parent) {
$bundle
->expects($this->any())
->method('getParent')
->will($this->returnValue($parent))
;
}
return $bundle;
}
protected function getKernel()
{
return $this
->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest')
->setMethods(array('getBundle', 'registerBundles'))
->disableOriginalConstructor()
->getMock()
;
}
protected function getKernelForInvalidLocateResource()
{
return $this
->getMockBuilder('Symfony\Component\HttpKernel\Kernel')
->disableOriginalConstructor()
->getMockForAbstractClass()
;
}
}
class KernelForTest extends Kernel
@ -33,6 +245,11 @@ class KernelForTest extends Kernel
$this->name = $name;
}
public function getBundleMap()
{
return $this->bundleMap;
}
public function registerRootDir()
{
}
@ -48,4 +265,9 @@ class KernelForTest extends Kernel
public function registerContainerConfiguration(LoaderInterface $loader)
{
}
}
public function initializeBundles()
{
parent::initializeBundles();
}
}

View File

@ -15,6 +15,7 @@ use Symfony\Component\Routing\Loader\LoaderResolver;
use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Loader\FileLocator;
class AnnotationDirectoryLoaderTest extends \PHPUnit_Framework_TestCase
{
@ -27,7 +28,7 @@ class AnnotationDirectoryLoaderTest extends \PHPUnit_Framework_TestCase
->disableOriginalConstructor()
->getMockForAbstractClass();
$loader = new AnnotationDirectoryLoader($annotationClassLoader);
$loader = new AnnotationDirectoryLoader(new FileLocator(), $annotationClassLoader);
$fixturesDir = __DIR__.'/../Fixtures';

View File

@ -27,7 +27,7 @@ class AnnotationFileLoaderTest extends \PHPUnit_Framework_TestCase
->disableOriginalConstructor()
->getMockForAbstractClass();
$loader = new AnnotationFileLoader($annotationClassLoader);
$loader = new AnnotationFileLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator'), $annotationClassLoader);
$fixture = __DIR__.'/../Fixtures/annotated.php';

View File

@ -27,7 +27,7 @@ class AnnotationGlobLoaderTest extends \PHPUnit_Framework_TestCase
->disableOriginalConstructor()
->getMockForAbstractClass();
$loader = new AnnotationGlobLoader($annotationClassLoader);
$loader = new AnnotationGlobLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator'), $annotationClassLoader);
$this->assertTrue($loader->supports('*'), '->supports() returns true if the resource is loadable');
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');

View File

@ -49,7 +49,7 @@ class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase
public function testSupports()
{
$resolver = new LoaderResolver(array(
$ini = new XmlFileLoader(array()),
$ini = new XmlFileLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator')),
));
$loader = new DelegatingLoader($resolver);

View File

@ -11,61 +11,26 @@
namespace Symfony\Tests\Component\Routing\Loader;
use Symfony\Component\Routing\Loader\FileLoader;
use Symfony\Component\Routing\Loader\FileLocator;
class FileLoaderTest extends \PHPUnit_Framework_TestCase
class FileLocatorTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Symfony\Component\Routing\Loader\FileLoader::__construct
*/
public function testConstructor()
{
$loader = new ProjectLoader(__DIR__);
$this->assertEquals(array(__DIR__), $loader->paths, '__construct() takes a path as its second argument');
$loader = new ProjectLoader(array(__DIR__, __DIR__));
$this->assertEquals(array(__DIR__, __DIR__), $loader->paths, '__construct() takes an array of paths as its second argument');
}
/**
* @covers Symfony\Component\Routing\Loader\FileLoader::GetAbsolutePath
* @covers Symfony\Component\Routing\Loader\FileLoader::isAbsolutePath
* @covers Symfony\Component\Routing\Loader\FileLocator::GetAbsolutePath
* @covers Symfony\Component\Routing\Loader\FileLocator::isAbsolutePath
*/
public function testGetAbsolutePath()
{
$loader = new ProjectLoader(array(__DIR__.'/../Fixtures'));
$loader = new FileLocator(array(__DIR__.'/../Fixtures'));
$this->assertEquals('/foo.xml', $loader->getAbsolutePath('/foo.xml'), '->getAbsolutePath() return the path unmodified if it is already an absolute path');
$this->assertEquals('c:\\\\foo.xml', $loader->getAbsolutePath('c:\\\\foo.xml'), '->getAbsolutePath() return the path unmodified if it is already an absolute path');
$this->assertEquals('c:/foo.xml', $loader->getAbsolutePath('c:/foo.xml'), '->getAbsolutePath() return the path unmodified if it is already an absolute path');
$this->assertEquals('\\server\\foo.xml', $loader->getAbsolutePath('\\server\\foo.xml'), '->getAbsolutePath() return the path unmodified if it is already an absolute path');
$this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'FileLoaderTest.php', $loader->getAbsolutePath('FileLoaderTest.php', __DIR__), '->getAbsolutePath() returns an absolute filename if the file exists in the current path');
$this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'FileLocatorTest.php', $loader->getAbsolutePath('FileLocatorTest.php', __DIR__), '->getAbsolutePath() returns an absolute filename if the file exists in the current path');
$this->assertEquals(__DIR__.'/../Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', $loader->getAbsolutePath('foo.xml', __DIR__), '->getAbsolutePath() returns an absolute filename if the file exists in one of the paths given in the constructor');
$this->assertEquals('foobar.xml', $loader->getAbsolutePath('foobar.xml', __DIR__), '->getAbsolutePath() returns the path unmodified if it is unable to find it in the given paths');
}
}
class ProjectLoader extends FileLoader
{
public $paths;
public function load($resource, $type = null)
{
}
public function supports($resource, $type = null)
{
return true;
}
public function getType()
{
}
public function getAbsolutePath($file, $currentPath = null)
{
return parent::getAbsolutePath($file, $currentPath);
}
}

View File

@ -35,7 +35,7 @@ class LoaderTest extends \PHPUnit_Framework_TestCase
public function testResolve()
{
$resolver = new LoaderResolver(array(
$ini = new XmlFileLoader(array()),
$ini = new XmlFileLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator')),
));
$loader = new ProjectLoader1();
$loader->setResolver($resolver);

View File

@ -23,7 +23,7 @@ class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
*/
public function testSupports()
{
$loader = new PhpFileLoader();
$loader = new PhpFileLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator'));
$this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable');
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');

View File

@ -23,7 +23,7 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
*/
public function testSupports()
{
$loader = new XmlFileLoader();
$loader = new XmlFileLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator'));
$this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable');
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');

View File

@ -16,6 +16,7 @@ use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Resource\FileResource;
use Symfony\Component\Routing\Loader\FileLocator;
class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
{
@ -24,7 +25,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
*/
public function testSupports()
{
$loader = new YamlFileLoader();
$loader = new YamlFileLoader($this->getMock('Symfony\Component\Routing\Loader\FileLocator'));
$this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable');
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
@ -35,7 +36,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
public function testLoadDoesNothingIfEmpty()
{
$loader = new YamlFileLoader(array(__DIR__.'/../Fixtures'));
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
$collection = $loader->load('empty.yml');
$this->assertEquals(array(), $collection->all());
@ -47,7 +48,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
*/
public function testLoadThrowsExceptionIfNotAnArray()
{
$loader = new YamlFileLoader(array(__DIR__.'/../Fixtures'));
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
$loader->load('nonvalid.yml');
}
}

View File

@ -16,7 +16,7 @@ require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\Loader\CacheLoader;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\TemplateNameParser;
class CacheLoaderTest extends \PHPUnit_Framework_TestCase
{

View File

@ -16,7 +16,7 @@ require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\ChainLoader;
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\TemplateNameParser;
class ChainLoaderTest extends \PHPUnit_Framework_TestCase
{

View File

@ -15,7 +15,7 @@ require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\TemplateNameParser;
class FilesystemLoaderTest extends \PHPUnit_Framework_TestCase
{

View File

@ -14,7 +14,7 @@ namespace Symfony\Tests\Component\Templating\Loader;
require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\TemplateNameParser;
class LoaderTest extends \PHPUnit_Framework_TestCase
{

View File

@ -18,7 +18,7 @@ use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\Helper\SlotsHelper;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\TemplateNameParser;
class PhpEngineTest extends \PHPUnit_Framework_TestCase
{