diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5151325450022..6cad90e26cdc2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -24,7 +24,8 @@ ./src/Symfony/Bundle/*/Resources ./src/Symfony/Component/*/Resources - ./src/Symfony/Component/HttpKernel/bootstrap* + ./src/Symfony/Component/HttpKernel/bootstrap.php + ./src/Symfony/Component/HttpKernel/bootstrap_cache.php diff --git a/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php b/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php index fbdb2663aa5fb..9e1158984b4f7 100644 --- a/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php +++ b/src/Symfony/Bundle/CompatAssetsBundle/CompatAssetsBundle.php @@ -20,19 +20,4 @@ */ class CompatAssetsBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineAbstractBundle/Common/DataFixtures/Loader.php b/src/Symfony/Bundle/DoctrineAbstractBundle/Common/DataFixtures/Loader.php new file mode 100644 index 0000000000000..1e7c2f754b2f5 --- /dev/null +++ b/src/Symfony/Bundle/DoctrineAbstractBundle/Common/DataFixtures/Loader.php @@ -0,0 +1,27 @@ +container = $container; + } + + public function addFixture(FixtureInterface $fixture) + { + if ($fixture instanceof ContainerAwareInterface) { + $fixture->setContainer($this->container); + } + + parent::addFixture($fixture); + } +} diff --git a/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/Common/ContainerAwareFixture.php b/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/Common/ContainerAwareFixture.php new file mode 100644 index 0000000000000..51a873cd56dac --- /dev/null +++ b/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/Common/ContainerAwareFixture.php @@ -0,0 +1,21 @@ +container = $container; + } + + public function load($manager) + { + } +} diff --git a/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/Common/DataFixtures/LoaderTest.php b/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/Common/DataFixtures/LoaderTest.php new file mode 100644 index 0000000000000..8bef32a6e230c --- /dev/null +++ b/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/Common/DataFixtures/LoaderTest.php @@ -0,0 +1,21 @@ +getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $loader = new Loader($container); + $fixture = new ContainerAwareFixture(); + + $loader->addFixture($fixture); + + $this->assertSame($container, $fixture->container); + } +} diff --git a/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/TestCase.php b/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/TestCase.php new file mode 100644 index 0000000000000..17326b6d5a876 --- /dev/null +++ b/src/Symfony/Bundle/DoctrineAbstractBundle/Tests/TestCase.php @@ -0,0 +1,13 @@ +markTestSkipped('Doctrine Data Fixtures is not available.'); + } + } +} diff --git a/src/Symfony/Bundle/DoctrineBundle/Command/LoadDataFixturesDoctrineCommand.php b/src/Symfony/Bundle/DoctrineBundle/Command/LoadDataFixturesDoctrineCommand.php index f0557b34eff04..3a369bbd61ef7 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Command/LoadDataFixturesDoctrineCommand.php +++ b/src/Symfony/Bundle/DoctrineBundle/Command/LoadDataFixturesDoctrineCommand.php @@ -18,8 +18,11 @@ use Symfony\Component\Console\Output\Output; use Symfony\Component\Finder\Finder; use Symfony\Bundle\FrameworkBundle\Util\Filesystem; +use Symfony\Bundle\DoctrineAbstractBundle\Common\DataFixtures\Loader as DataFixturesLoader; use Doctrine\Common\Cli\Configuration; use Doctrine\Common\Cli\CliController as DoctrineCliController; +use Doctrine\Common\DataFixtures\Executor\ORMExecutor; +use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Internal\CommitOrderCalculator; use Doctrine\ORM\Mapping\ClassMetadata; @@ -72,15 +75,15 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - $loader = new \Doctrine\Common\DataFixtures\Loader(); + $loader = new DataFixturesLoader($this->container); foreach ($paths as $path) { if (is_dir($path)) { $loader->loadFromDirectory($path); } } $fixtures = $loader->getFixtures(); - $purger = new \Doctrine\Common\DataFixtures\Purger\ORMPurger($em); - $executor = new \Doctrine\Common\DataFixtures\Executor\ORMExecutor($em, $purger); + $purger = new ORMPurger($em); + $executor = new ORMExecutor($em, $purger); $executor->setLogger(function($message) use ($output) { $output->writeln(sprintf(' > %s', $message)); }); diff --git a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php index 813c6033727a7..55fc875889407 100755 --- a/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php +++ b/src/Symfony/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php @@ -408,6 +408,7 @@ protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $ new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])) ); $ormEmDef = new Definition('%doctrine.orm.entity_manager_class%', $ormEmArgs); + $ormEmDef->setFactoryClass('%doctrine.orm.entity_manager_class%'); $ormEmDef->setFactoryMethod('create'); $ormEmDef->addTag('doctrine.orm.entity_manager'); $container->setDefinition($entityManagerService, $ormEmDef); diff --git a/src/Symfony/Bundle/DoctrineBundle/DoctrineBundle.php b/src/Symfony/Bundle/DoctrineBundle/DoctrineBundle.php index 5e4e6ab21507e..de1d170543fde 100644 --- a/src/Symfony/Bundle/DoctrineBundle/DoctrineBundle.php +++ b/src/Symfony/Bundle/DoctrineBundle/DoctrineBundle.php @@ -31,20 +31,4 @@ public function registerExtensions(ContainerBuilder $container) $container->addCompilerPass(new RegisterEventListenersAndSubscribersPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION); } - - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php index bd9fbefc9eaff..46a4e4046771b 100755 --- a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php @@ -157,6 +157,7 @@ public function testDependencyInjectionConfigurationDefaults() $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.orm.entity_manager', $definition->getTags()); @@ -198,6 +199,7 @@ public function testSingleEntityManagerConfiguration() $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.orm.entity_manager', $definition->getTags()); @@ -239,6 +241,7 @@ public function testLoadSimpleSingleConnection() $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.orm.entity_manager', $definition->getTags()); @@ -279,6 +282,7 @@ public function testLoadSingleConnection() $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.orm.entity_manager', $definition->getTags()); @@ -313,6 +317,7 @@ public function testLoadMultipleConnections() $definition = $container->getDefinition('doctrine.orm.dm1_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.orm.entity_manager', $definition->getTags()); @@ -334,6 +339,7 @@ public function testLoadMultipleConnections() $definition = $container->getDefinition('doctrine.orm.dm2_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.orm.entity_manager', $definition->getTags()); diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php index 3cd2803b1f05a..899d3e178eb0f 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php @@ -6,19 +6,4 @@ class AnnotationsBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } -} \ No newline at end of file +} diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php index 3e08c3a86e8b8..641edc122e105 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php @@ -6,19 +6,4 @@ class AnnotationsBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } -} \ No newline at end of file +} diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php index 9980c7bef12ea..3bdd9873eaa29 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php @@ -6,19 +6,4 @@ class XmlBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php index 225ce0f90368f..c89784c34c4ac 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php +++ b/src/Symfony/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php @@ -6,19 +6,4 @@ class YamlBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineMigrationsBundle/DoctrineMigrationsBundle.php b/src/Symfony/Bundle/DoctrineMigrationsBundle/DoctrineMigrationsBundle.php index d987d30d79cda..6fadd42c11c35 100644 --- a/src/Symfony/Bundle/DoctrineMigrationsBundle/DoctrineMigrationsBundle.php +++ b/src/Symfony/Bundle/DoctrineMigrationsBundle/DoctrineMigrationsBundle.php @@ -21,19 +21,4 @@ */ class DoctrineMigrationsBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/LoadDataFixturesDoctrineODMCommand.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/LoadDataFixturesDoctrineODMCommand.php index 51609ab4d3771..bcaf9e0d85a16 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/LoadDataFixturesDoctrineODMCommand.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Command/LoadDataFixturesDoctrineODMCommand.php @@ -18,8 +18,11 @@ use Symfony\Component\Console\Output\Output; use Symfony\Component\Finder\Finder; use Symfony\Bundle\FrameworkBundle\Util\Filesystem; +use Symfony\Bundle\DoctrineAbstractBundle\Common\DataFixtures\Loader as DataFixturesLoader; use Doctrine\Common\Cli\Configuration; use Doctrine\Common\Cli\CliController as DoctrineCliController; +use Doctrine\Common\DataFixtures\Executor\MongoDBExecutor; +use Doctrine\Common\DataFixtures\Purger\MongoDBPurger; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Internal\CommitOrderCalculator; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; @@ -72,15 +75,15 @@ protected function execute(InputInterface $input, OutputInterface $output) } } - $paths = array_filter($paths, 'is_dir'); - - $loader = new \Doctrine\Common\DataFixtures\Loader(); + $loader = new DataFixturesLoader($this->container); foreach ($paths as $path) { - $loader->loadFromDirectory($path); + if (is_dir($path)) { + $loader->loadFromDirectory($path); + } } $fixtures = $loader->getFixtures(); - $purger = new \Doctrine\Common\DataFixtures\Purger\MongoDBPurger($dm); - $executor = new \Doctrine\Common\DataFixtures\Executor\MongoDBExecutor($dm, $purger); + $purger = new MongoDBPurger($dm); + $executor = new MongoDBExecutor($dm, $purger); $executor->setLogger(function($message) use ($output) { $output->writeln(sprintf(' > %s', $message)); }); diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/DoctrineMongoDBExtension.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/DoctrineMongoDBExtension.php index 880005e174879..0eb11749b3f48 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/DoctrineMongoDBExtension.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/DependencyInjection/DoctrineMongoDBExtension.php @@ -165,6 +165,7 @@ protected function loadDocumentManager(array $documentManager, ContainerBuilder new Reference($eventManagerId), ); $odmDmDef = new Definition('%doctrine.odm.mongodb.document_manager_class%', $odmDmArgs); + $odmDmDef->setFactoryClass('%doctrine.odm.mongodb.document_manager_class%'); $odmDmDef->setFactoryMethod('create'); $odmDmDef->addTag('doctrine.odm.mongodb.document_manager'); $container->setDefinition(sprintf('doctrine.odm.mongodb.%s_document_manager', $documentManager['name']), $odmDmDef); diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/DoctrineMongoDBBundle.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/DoctrineMongoDBBundle.php index d6d409c473b95..99e205e977a5f 100755 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/DoctrineMongoDBBundle.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/DoctrineMongoDBBundle.php @@ -35,20 +35,4 @@ public function registerExtensions(ContainerBuilder $container) $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 __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php index b500b08e06350..dee13dabc7669 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/AbstractMongoDBExtensionTest.php @@ -65,6 +65,7 @@ public function testDependencyInjectionConfigurationDefaults() $definition = $container->getDefinition('doctrine.odm.mongodb.default_document_manager'); $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.odm.mongodb.document_manager', $definition->getTags()); @@ -92,6 +93,7 @@ public function testSingleDocumentManagerConfiguration() $definition = $container->getDefinition('doctrine.odm.mongodb.default_document_manager'); $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.odm.mongodb.document_manager', $definition->getTags()); @@ -126,6 +128,7 @@ public function testLoadSimpleSingleConnection() $definition = $container->getDefinition('doctrine.odm.mongodb.default_document_manager'); $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.odm.mongodb.document_manager', $definition->getTags()); @@ -154,6 +157,7 @@ public function testLoadSingleConnection() $definition = $container->getDefinition('doctrine.odm.mongodb.default_document_manager'); $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.odm.mongodb.document_manager', $definition->getTags()); @@ -184,6 +188,7 @@ public function testLoadMultipleConnections() $definition = $container->getDefinition('doctrine.odm.mongodb.dm1_document_manager'); $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.odm.mongodb.document_manager', $definition->getTags()); @@ -199,6 +204,7 @@ public function testLoadMultipleConnections() $definition = $container->getDefinition('doctrine.odm.mongodb.dm2_document_manager'); $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getClass()); + $this->assertEquals('%doctrine.odm.mongodb.document_manager_class%', $definition->getFactoryClass()); $this->assertEquals('create', $definition->getFactoryMethod()); $this->assertArrayHasKey('doctrine.odm.mongodb.document_manager', $definition->getTags()); diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php index 2c4eee1523a52..23b96a825d60f 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php @@ -6,19 +6,4 @@ class AnnotationsBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php index bdb28af65133b..f02676fcb8d6d 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php @@ -6,19 +6,4 @@ class XmlBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php index 8c06610c5eb1e..3c63ccb389c15 100644 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php @@ -6,19 +6,4 @@ class YamlBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/Validator/Constraints/UniqueValidatorTest.php b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/Validator/Constraints/UniqueValidatorTest.php index 4af23affe6d8c..f3e0d6fae7e1c 100755 --- a/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/Validator/Constraints/UniqueValidatorTest.php +++ b/src/Symfony/Bundle/DoctrineMongoDBBundle/Tests/Validator/Constraints/UniqueValidatorTest.php @@ -5,10 +5,11 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\DocumentRepository; use Symfony\Bundle\DoctrineMongoDBBundle\Tests\Fixtures\Validator\Document; +use Symfony\Bundle\DoctrineMongoDBBundle\Tests\TestCase; use Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints\Unique; use Symfony\Bundle\DoctrineMongoDBBundle\Validator\Constraints\UniqueValidator; -class UniqueValidatorTest extends \PHPUnit_Framework_TestCase +class UniqueValidatorTest extends TestCase { private $dm; private $repository; @@ -18,6 +19,7 @@ class UniqueValidatorTest extends \PHPUnit_Framework_TestCase public function setUp() { + parent::setUp(); $this->classMetadata = $this->getClassMetadata(); $this->repository = $this->getDocumentRepository(); $this->dm = $this->getDocumentManager($this->classMetadata, $this->repository); diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php index 3477454468d35..032b927804292 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php @@ -64,7 +64,7 @@ protected function computeTemplatePaths() $prefix = '/Resources/views'; $templates = array(); foreach ($this->kernel->getBundles() as $name => $bundle) { - if (!is_dir($dir = $bundle->getNormalizedPath().$prefix)) { + if (!is_dir($dir = $bundle->getPath().$prefix)) { continue; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/InitBundleCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/InitBundleCommand.php index deb6a1639befb..500eb99eda21c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/InitBundleCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/InitBundleCommand.php @@ -71,16 +71,18 @@ protected function execute(InputInterface $input, OutputInterface $output) // user specified bundle name? $bundle = $input->getArgument('bundleName'); - if ('' === $bundle) { + if (!$bundle) { $bundle = strtr($namespace, array('\\' => '')); - } elseif (!preg_match('/Bundle$/', $bundle)) { + } + + if (!preg_match('/Bundle$/', $bundle)) { throw new \InvalidArgumentException('The bundle name must end with Bundle.'); } $dir = $input->getArgument('dir'); // add trailing / if necessary - $dir = '/' === substr($dir, -1, 1) ? $dir : $dir . '/'; + $dir = '/' === substr($dir, -1, 1) ? $dir : $dir.'/'; $targetDir = $dir.strtr($namespace, '\\', '/'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php index 118bbfaeb6696..806bd1835ef37 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -34,9 +34,11 @@ public function __construct(KernelInterface $kernel) { $this->kernel = $kernel; - parent::__construct('Symfony', Kernel::VERSION.' - '.$kernel->getName()); + parent::__construct('Symfony', Kernel::VERSION.' - '.$kernel->getName().'/'.$kernel->getEnvironment().($kernel->isDebug() ? '/debug' : '')); $this->definition->addOption(new InputOption('--shell', '-s', InputOption::VALUE_NONE, 'Launch the shell.')); + $this->definition->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', 'dev')); + $this->definition->addOption(new InputOption('--debug', '-d', InputOption::VALUE_NONE, 'Whether to run in debug mode.')); $this->kernel->boot(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index cb094a3d6d37c..0f95d187a1a31 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -47,7 +47,7 @@ public function redirectAction($route, $permanent = false) $code = $permanent ? 301 : 302; $attributes = $this->container->get('request')->attributes->all(); - unset($attributes['_route'], $attributes['route']); + unset($attributes['_route'], $attributes['route'], $attributes['permanent'] ); $response = $this->container->get('response'); $response->setRedirect($this->container->get('router')->generate($route, $attributes), $code); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000000000..066d1f3c829a9 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -0,0 +1,222 @@ + + */ +class Configuration +{ + /** + * Generates the configuration tree. + * + * @param boolean $kernelDebug The kernel.debug DIC parameter + * @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface + */ + public function getConfigTree($kernelDebug) + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('app:config', 'array'); + + $rootNode + ->scalarNode('cache_warmer')->defaultValue(!$kernelDebug)->end() + ->scalarNode('charset')->end() + ->scalarNode('document_root')->end() + ->scalarNode('error_handler')->end() + ->scalarNode('exception_controller')->end() + ->scalarNode('ide')->end() + ->booleanNode('test')->end() + ; + + $this->addCsrfProtectionSection($rootNode); + $this->addEsiSection($rootNode); + $this->addProfilerSection($rootNode); + $this->addRouterSection($rootNode); + $this->addSessionSection($rootNode); + $this->addTemplatingSection($rootNode); + $this->addTranslatorSection($rootNode); + $this->addValidationSection($rootNode); + + return $treeBuilder->buildTree(); + } + + private function addCsrfProtectionSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('csrf_protection') + ->canBeUnset() + ->treatNullLike(array('enabled' => true)) + ->treatTrueLike(array('enabled' => true)) + ->booleanNode('enabled')->end() + ->scalarNode('field_name')->end() + ->scalarNode('secret')->end() + ->end() + ; + } + + private function addEsiSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('esi') + ->canBeUnset() + ->treatNullLike(array('enabled' => true)) + ->treatTrueLike(array('enabled' => true)) + ->booleanNode('enabled')->end() + ->end() + ; + } + + private function addProfilerSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('profiler') + ->canBeUnset() + ->treatNullLike(array()) + ->treatTrueLike(array()) + ->booleanNode('only_exceptions')->end() + ->arrayNode('matcher') + ->canBeUnset() + ->scalarNode('ip')->end() + ->scalarNode('path')->end() + ->scalarNode('service')->end() + ->end() + ->end() + ; + } + + private function addRouterSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('router') + ->canBeUnset() + ->scalarNode('cache_warmer')->end() + ->scalarNode('resource')->isRequired()->end() + ->scalarNode('type')->end() + ->end() + ; + } + + private function addSessionSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('session') + ->canBeUnset() + ->treatNullLike(array()) + ->treatTrueLike(array()) + // Strip "pdo." prefix from option keys, since dots cannot appear in node names + ->beforeNormalization() + ->ifArray() + ->then(function($v){ + foreach ($v as $key => $value) { + if (0 === strncmp('pdo.', $key, 4)) { + $v[substr($key, 4)] = $value; + unset($v[$key]); + } + } + return $v; + }) + ->end() + ->booleanNode('auto_start')->end() + ->scalarNode('class')->end() + ->scalarNode('default_locale')->end() + ->scalarNode('storage_id')->defaultValue('native')->end() + // NativeSessionStorage options + ->scalarNode('name')->end() + ->scalarNode('lifetime')->end() + ->scalarNode('path')->end() + ->scalarNode('domain')->end() + ->booleanNode('secure')->end() + ->booleanNode('httponly')->end() + // PdoSessionStorage options + ->scalarNode('db_table')->end() + ->scalarNode('db_id_col')->end() + ->scalarNode('db_data_col')->end() + ->scalarNode('db_time_col')->end() + ->end() + ; + } + + private function addTemplatingSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('templating') + ->canBeUnset() + ->scalarNode('assets_version')->end() + ->scalarNode('assets_base_urls')->end() + ->scalarNode('cache')->end() + ->scalarNode('cache_warmer')->end() + ->fixXmlConfig('engine') + ->arrayNode('engines') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function($v){ return !is_array($v); }) + ->then(function($v){ return array($v); }) + ->end() + ->prototype('scalar') + ->beforeNormalization() + ->ifTrue(function($v) { return is_array($v) && isset($v['id']); }) + ->then(function($v){ return $v['id']; }) + ->end() + ->end() + ->end() + ->fixXmlConfig('loader') + ->arrayNode('loaders') + ->beforeNormalization() + ->ifTrue(function($v){ return !is_array($v); }) + ->then(function($v){ return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ; + } + + private function addTranslatorSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('translator') + ->canBeUnset() + ->booleanNode('enabled')->defaultTrue()->end() + ->scalarNode('fallback')->end() + ->end() + ; + } + + private function addValidationSection(NodeBuilder $rootNode) + { + $rootNode + ->arrayNode('validation') + ->canBeUnset() + // For XML, namespace is a child of validation, so it must be moved under annotations + ->beforeNormalization() + ->ifTrue(function($v) { return is_array($v) && !empty($v['annotations']) && !empty($v['namespace']); }) + ->then(function($v){ + $v['annotations'] = array('namespace' => $v['namespace']); + return $v; + }) + ->end() + ->booleanNode('enabled')->end() + ->arrayNode('annotations') + ->canBeUnset() + ->treatNullLike(array()) + ->treatTrueLike(array()) + ->fixXmlConfig('namespace') + ->arrayNode('namespaces') + ->containsNameValuePairsWithKeyAttribute('prefix') + ->prototype('scalar') + ->beforeNormalization() + ->ifTrue(function($v) { return is_array($v) && isset($v['namespace']); }) + ->then(function($v){ return $v['namespace']; }) + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ecbeae5bb72d7..d6121b0fc00dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -11,150 +11,122 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Configuration\Processor; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Resource\FileResource; use Symfony\Component\Finder\Finder; -use Symfony\Component\HttpFoundation\RequestMatcher; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\Form\FormContext; /** * FrameworkExtension. * * @author Fabien Potencier + * @author Jeremy Mikola */ class FrameworkExtension extends Extension { - public function configLoad(array $configs, ContainerBuilder $container) - { - foreach ($configs as $config) { - $this->doConfigLoad($config, $container); - } - } - /** - * Loads the web configuration. + * Responds to the app.config configuration parameter. * - * @param array $config An array of configuration settings - * @param ContainerBuilder $container A ContainerBuilder instance + * @param array $configs + * @param ContainerBuilder $container */ - protected function doConfigLoad(array $config, ContainerBuilder $container) + public function configLoad(array $configs, ContainerBuilder $container) { $loader = new XmlFileLoader($container, __DIR__.'/../Resources/config'); - if (!$container->hasDefinition('controller_resolver')) { - $loader->load('web.xml'); - } + $loader->load('web.xml'); + $loader->load('form.xml'); + $loader->load('services.xml'); - if (!$container->hasDefinition('form.factory')) { - $loader->load('form.xml'); - } + // A translator must always be registered (as support is included by + // default in the Form component). If disabled, an identity translator + // will be used and everything will still work as expected. + $loader->load('translation.xml'); - if (isset($config['csrf-protection'])) { - $config['csrf_protection'] = $config['csrf-protection']; + if ($container->getParameter('kernel.debug')) { + $loader->load('debug.xml'); + $container->setDefinition('event_dispatcher', $container->findDefinition('debug.event_dispatcher')); + $container->setAlias('debug.event_dispatcher', 'event_dispatcher'); } - if (isset($config['csrf_protection'])) { - foreach (array('enabled', 'field_name', 'field-name', 'secret') as $key) { - if (isset($config['csrf_protection'][$key])) { - $container->setParameter('form.csrf_protection.'.strtr($key, '-', '_'), - $config['csrf_protection'][$key]); - } - } - } + $processor = new Processor(); + $configuration = new Configuration(); - if (isset($config['ide'])) { - switch ($config['ide']) { - case 'textmate': - $pattern = 'txmt://open?url=file://%%f&line=%%l'; - break; - - case 'macvim': - $pattern = 'mvim://open?url=file://%%f&line=%%l'; - break; - - default: - // should be the link pattern then - $pattern = $config['ide']; - } + $config = $processor->process($configuration->getConfigTree($container->getParameter('kernel.debug')), $configs); - $container->setParameter('debug.file_link_format', $pattern); - } + $container->setParameter('kernel.cache_warmup', $config['cache_warmer']); - foreach (array('document_root', 'document-root') as $key) { - if (isset($config[$key])) { - $container->setParameter('document_root', $config[$key]); - } + if (isset($config['charset'])) { + $container->setParameter('kernel.charset', $config['charset']); } - if (!$container->hasDefinition('event_dispatcher')) { - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('services.xml'); + if (isset($config['document_root'])) { + $container->setParameter('document_root', $config['document_root']); + } - if ($container->getParameter('kernel.debug')) { - $loader->load('debug.xml'); - $container->setDefinition('event_dispatcher', $container->findDefinition('debug.event_dispatcher')); - $container->setAlias('debug.event_dispatcher', 'event_dispatcher'); + if (isset($config['error_handler'])) { + if (false === $config['error_handler']) { + $container->getDefinition('error_handler')->setMethodCalls(array()); + } else { + $container->getDefinition('error_handler')->addMethodCall('register', array()); + $container->setParameter('error_handler.level', $config['error_handler']); } } - if (isset($config['charset'])) { - $container->setParameter('kernel.charset', $config['charset']); + if (isset($config['exception_controller'])) { + $container->setParameter('exception_listener.controller', $config['exception_controller']); } - foreach (array('error_handler', 'error-handler') as $key) { - if (array_key_exists($key, $config)) { - if (false === $config[$key]) { - $container->getDefinition('error_handler')->setMethodCalls(array()); - } else { - $container->getDefinition('error_handler')->addMethodCall('register', array()); - $container->setParameter('error_handler.level', $config[$key]); - } - } + if (isset($config['ide'])) { + $patterns = array( + 'textmate' => 'txmt://open?url=file://%%f&line=%%l', + 'macvim' => 'mvim://open?url=file://%%f&line=%%l', + ); + $pattern = isset($patterns[$config['ide']]) ? $patterns[$config['ide']] : $config['ide']; + $container->setParameter('debug.file_link_format', $pattern); } - if (isset($config['router'])) { - $this->registerRouterConfiguration($config, $container); + if (isset($config['test']) && $config['test']) { + $loader->load('test.xml'); + $config['session']['storage_id'] = 'array'; } - if (isset($config['profiler'])) { - $this->registerProfilerConfiguration($config, $container); + if (isset($config['csrf_protection'])) { + $this->registerCsrfProtectionConfiguration($config['csrf_protection'], $container); } - if (isset($config['validation']['enabled'])) { - $this->registerValidationConfiguration($config, $container); + if (isset($config['esi'])) { + $this->registerEsiConfiguration($config['esi'], $loader); } - if (array_key_exists('templating', $config)) { - $this->registerTemplatingConfiguration($config, $container); + if (isset($config['profiler'])) { + $this->registerProfilerConfiguration($config['profiler'], $container, $loader); } - if (array_key_exists('test', $config)) { - $this->registerTestConfiguration($config, $container); + if (isset($config['router'])) { + $this->registerRouterConfiguration($config['router'], $container, $loader); } - if (array_key_exists('session', $config)) { - $this->registerSessionConfiguration($config, $container); + if (isset($config['session'])) { + $this->registerSessionConfiguration($config['session'], $container, $loader); } - // translator must always be registered (as support is included by default for forms for instance) - // if you disable it, an identity translator will be used and everything will still work as expected - $this->registerTranslatorConfiguration($config, $container); - - if (array_key_exists('esi', $config)) { - $this->registerEsiConfiguration($config, $container); + if (isset($config['templating'])) { + $this->registerTemplatingConfiguration($config['templating'], $container, $loader); } - if (isset($config['cache-warmer'])) { - $config['cache_warmer'] = $config['cache-warmer']; + if (isset($config['translator'])) { + $this->registerTranslatorConfiguration($config['translator'], $container); } - $warmer = isset($config['cache_warmer']) ? $config['cache_warmer'] : !$container->getParameter('kernel.debug'); - $container->setParameter('kernel.cache_warmup', $warmer); + if (isset($config['validation'])) { + $this->registerValidationConfiguration($config['validation'], $container, $loader); + } $this->addClassesToCompile(array( 'Symfony\\Component\\HttpFoundation\\ParameterBag', @@ -178,236 +150,119 @@ protected function doConfigLoad(array $config, ContainerBuilder $container) 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', 'Symfony\\Component\\EventDispatcher\\EventDispatcher', 'Symfony\\Bundle\\FrameworkBundle\\EventDispatcher', - - 'Symfony\\Component\\Form\\FormContext', - 'Symfony\\Component\\Form\\FormContextInterface', )); } /** - * Loads the templating configuration. + * Loads the CSRF protection configuration. * - * @param array $config An array of configuration settings + * @param array $config A CSRF protection configuration array * @param ContainerBuilder $container A ContainerBuilder instance */ - protected function registerTemplatingConfiguration(array $config, ContainerBuilder $container) + private function registerCsrfProtectionConfiguration(array $config, ContainerBuilder $container) { - $config = isset($config['templating']) ? $config['templating'] : array(); - - if (!$container->hasDefinition('templating.locator')) { - $loader = new XmlFileLoader($container, __DIR__.'/../Resources/config'); - $loader->load('templating.xml'); - $loader->load('templating_php.xml'); - - if ($container->getParameter('kernel.debug')) { - $loader->load('templating_debug.xml'); - } - } - - if (array_key_exists('assets-version', $config)) { - $container->setParameter('templating.assets.version', $config['assets-version']); - } - - if (array_key_exists('assets_version', $config)) { - $container->setParameter('templating.assets.version', $config['assets_version']); - } - - if (array_key_exists('assets-base-urls', $config)) { - $container->setParameter('templating.assets.base_urls', $config['assets-base-urls']); - } - - if (array_key_exists('assets_base_urls', $config)) { - $container->setParameter('templating.assets.base_urls', $config['assets_base_urls']); - } - - // loaders - if (isset($config['loader'])) { - $loaders = array(); - $ids = is_array($config['loader']) ? $config['loader'] : array($config['loader']); - foreach ($ids as $id) { - $loaders[] = new Reference($id); - } - - if (1 === count($loaders)) { - $container->setAlias('templating.loader', (string) $loaders[0]); - } else { - $container->getDefinition('templating.loader.chain')->addArgument($loaders); - $container->setAlias('templating.loader', 'templating.loader.chain'); - } - } - - // cache? - $container->setParameter('templating.loader.cache.path', null); - if (isset($config['cache'])) { - // wrap the loader with some cache - $container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader')); - $container->setDefinition('templating.loader', $container->getDefinition('templating.loader.cache')); - $container->setParameter('templating.loader.cache.path', $config['cache']); - } - - if (isset($config['cache-warmer'])) { - $config['cache_warmer'] = $config['cache-warmer']; - } - - if (isset($config['cache_warmer']) && $config['cache_warmer']) { - $container->getDefinition('templating.cache_warmer.template_paths')->addTag('kernel.cache_warmer'); - $container->setAlias('templating.locator', 'templating.locator.cached'); - } - - // engines - if (!$engines = $this->normalizeConfig($config, 'engine')) { - throw new \LogicException('You must register at least one templating engine.'); - } - - $this->addClassesToCompile(array( - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface', - 'Symfony\\Component\\Templating\\EngineInterface', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocatorInterface', - $container->findDefinition('templating.locator')->getClass(), - )); - - foreach ($engines as $i => $engine) { - $id = is_array($engine) ? $engine['id'] : $engine; - $engines[$i] = new Reference('templating.engine.'.$id); - - if ('php' === $id) { - $this->addClassesToCompile(array( - 'Symfony\\Component\\Templating\\PhpEngine', - 'Symfony\\Component\\Templating\\TemplateNameParserInterface', - 'Symfony\\Component\\Templating\\TemplateNameParser', - 'Symfony\\Component\\Templating\\Loader\\LoaderInterface', - 'Symfony\\Component\\Templating\\Storage\\Storage', - 'Symfony\\Component\\Templating\\Storage\\FileStorage', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', - 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', - )); + foreach (array('enabled', 'field_name', 'secret') as $key) { + if (isset($config[$key])) { + $container->setParameter('form.csrf_protection.'.$key, $config[$key]); } } - - if (1 === count($engines)) { - $container->setAlias('templating', (string) $engines[0]); - } else { - $def = $container->getDefinition('templating.engine.delegating'); - $def->setArgument(1, $engines); - - $container->setAlias('templating', 'templating.engine.delegating'); - } - } - - /** - * Loads the test configuration. - * - * @param array $config A configuration array - * @param ContainerBuilder $container A ContainerBuilder instance - */ - protected function registerTestConfiguration(array $config, ContainerBuilder $container) - { - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('test.xml'); - - $container->setAlias('session.storage', 'session.storage.array'); } /** * Loads the ESI configuration. * - * @param array $config A configuration array - * @param ContainerBuilder $container A ContainerBuilder instance + * @param array $config An ESI configuration array + * @param XmlFileLoader $loader An XmlFileLoader instance */ - protected function registerEsiConfiguration(array $config, ContainerBuilder $container) + private function registerEsiConfiguration(array $config, XmlFileLoader $loader) { - if (isset($config['esi']['enabled']) && $config['esi']['enabled']) { - if (!$container->hasDefinition('esi')) { - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('esi.xml'); - } + if (isset($config['enabled']) && $config['enabled']) { + $loader->load('esi.xml'); } } /** - * Loads the translator configuration. + * Loads the profiler configuration. * - * @param array $config A configuration array + * @param array $config A profiler configuration array * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance */ - protected function registerTranslatorConfiguration(array $config, ContainerBuilder $container) + private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - $first = false; - if (!$container->hasDefinition('translator')) { - $first = true; - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('translation.xml'); - } + $loader->load('profiling.xml'); + $loader->load('collectors.xml'); - $config = array_key_exists('translator', $config) ? $config['translator'] : array(); - if (!is_array($config)) { - $config = array(); + if (isset($config['only_exceptions'])) { + $container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']); } - if (!isset($config['translator']['enabled']) || $config['translator']['enabled']) { - // use the "real" translator - $container->setDefinition('translator', $container->findDefinition('translator.real')); + if (isset($config['matcher'])) { + if (isset($config['matcher']['service'])) { + $container->setAlias('profiler.request_matcher', $config['matcher']['service']); + } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path'])) { + $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher'); + $definition->setPublic(false); - if ($first) { - // translation directories - $dirs = array(); - foreach ($container->getParameter('kernel.bundles') as $bundle) { - $reflection = new \ReflectionClass($bundle); - if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) { - $dirs[] = $dir; - } - } - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/translations')) { - $dirs[] = $dir; + if (isset($config['matcher']['ip'])) { + $definition->addMethodCall('matchIp', array($config['matcher']['ip'])); } - // translation resources - $resources = array(); - if ($dirs) { - $finder = new Finder(); - $finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs); - foreach ($finder as $file) { - // filename is domain.locale.format - list($domain, $locale, $format) = explode('.', $file->getBasename()); - - $resources[] = array($format, (string) $file, $locale, $domain); - } + if (isset($config['matcher']['path'])) { + $definition->addMethodCall('matchPath', array($config['matcher']['path'])); } - $container->setParameter('translation.resources', $resources); } } - - if (array_key_exists('fallback', $config)) { - $container->setParameter('translator.fallback_locale', $config['fallback']); - } } /** - * Loads the session configuration. + * Loads the router configuration. * - * @param array $config A configuration array + * @param array $config A router configuration array * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + * @throws \InvalidArgumentException if resource option is not set */ - protected function registerSessionConfiguration(array $config, ContainerBuilder $container) + private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!$container->hasDefinition('session')) { - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('session.xml'); + $loader->load('routing.xml'); + + if (!isset($config['resource'])) { + throw new \InvalidArgumentException('Router configuration requires a resource option.'); } - $config = isset($config['session']) ? $config['session'] : array(); + $container->setParameter('routing.resource', $config['resource']); - foreach (array('default_locale', 'default-locale') as $key) { - if (isset($config[$key])) { - $container->setParameter('session.default_locale', $config[$key]); - } + if (isset($config['type'])) { + $container->setParameter('router.options.resource_type', $config['type']); } - if (isset($config['auto-start'])) { - $config['auto_start'] = $config['auto-start']; + if (isset($config['cache_warmer']) && $config['cache_warmer']) { + $container->getDefinition('router.cache_warmer')->addTag('kernel.cache_warmer'); + $container->setAlias('router', 'router.cached'); } + $this->addClassesToCompile(array( + 'Symfony\\Component\\Routing\\RouterInterface', + 'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface', + 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + 'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface', + 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + $container->findDefinition('router')->getClass(), + )); + } + + /** + * Loads the session configuration. + * + * @param array $config A session configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerSessionConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('session.xml'); + if (isset($config['auto_start']) && $config['auto_start']) { $container->getDefinition('session')->addMethodCall('start'); } @@ -416,29 +271,19 @@ protected function registerSessionConfiguration(array $config, ContainerBuilder $container->setParameter('session.class', $config['class']); } - if (isset($config['storage-id'])) { - $config['storage_id'] = $config['storage-id']; + if (isset($config['default_locale'])) { + $container->setParameter('session.default_locale', $config['default_locale']); } - if (isset($config['storage_id'])) { - $container->setAlias('session.storage', 'session.storage.'.$config['storage_id']); - } else { - $config['storage_id'] = 'native'; - } - - $options = $container->getParameter('session.storage.'.strtolower($config['storage_id']).'.options'); - foreach (array('name', 'lifetime', 'path', 'domain', 'secure', 'httponly', 'cache_limiter', 'pdo.db_table', 'pdo.db_id_col', 'pdo.db_data_col', 'pdo.db_time_col') as $name) { - $key = str_replace('pdo.', '', $name); - if (isset($config[$name])) { - $options[$key] = $config[$name]; - } + $container->setAlias('session.storage', 'session.storage.'.$config['storage_id']); - $nName = str_replace('_', '-', $name); - if (isset($config[$nName])) { - $options[$key] = $config[$nName]; + $options = $container->getParameter('session.storage.'.$config['storage_id'].'.options'); + foreach (array('name', 'lifetime', 'path', 'domain', 'secure', 'httponly', 'db_table', 'db_id_col', 'db_data_col', 'db_time_col') as $key) { + if (isset($config[$key])) { + $options[$key] = $config[$key]; } } - $container->setParameter('session.storage.'.strtolower($config['storage_id']).'.options', $options); + $container->setParameter('session.storage.'.$config['storage_id'].'.options', $options); $this->addClassesToCompile(array( 'Symfony\\Component\\HttpFoundation\\Session', @@ -448,167 +293,204 @@ protected function registerSessionConfiguration(array $config, ContainerBuilder } /** - * Loads the router configuration. + * Loads the templating configuration. * - * @param array $config A configuration array + * @param array $config A templating configuration array * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + * @throws \LogicException if no engines are defined */ - protected function registerRouterConfiguration(array $config, ContainerBuilder $container) + private function registerTemplatingConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!$container->hasDefinition('router')) { - $loader = new XmlFileLoader($container, __DIR__.'/../Resources/config'); - $loader->load('routing.xml'); + $loader->load('templating.xml'); + $loader->load('templating_php.xml'); + + if ($container->getParameter('kernel.debug')) { + $loader->load('templating_debug.xml'); } - $container->setParameter('routing.resource', $config['router']['resource']); + if (isset($config['assets_version'])) { + $container->setParameter('templating.assets.version', $config['assets_version']); + } - if (isset($config['router']['cache-warmer'])) { - $config['router']['cache_warmer'] = $config['router']['cache-warmer']; + if (isset($config['assets_base_urls'])) { + $container->setParameter('templating.assets.base_urls', $config['assets_base_urls']); } - if (isset($config['router']['cache_warmer']) && $config['router']['cache_warmer']) { - $container->getDefinition('router.cache_warmer')->addTag('kernel.cache_warmer'); - $container->setAlias('router', 'router.cached'); + if (isset($config['loaders']) && $config['loaders']) { + $loaders = array_map(function($loader) { return new Reference($loader); }, $config['loaders']); + + // Use a delegation unless only a single loader was registered + if (1 === count($loaders)) { + $container->setAlias('templating.loader', (string) reset($loaders)); + } else { + $container->getDefinition('templating.loader.chain')->addArgument($loaders); + $container->setAlias('templating.loader', 'templating.loader.chain'); + } + } + + if (isset($config['cache'])) { + // Wrap the existing loader with cache (must happen after loaders are registered) + $container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader')); + $container->setDefinition('templating.loader', $container->getDefinition('templating.loader.cache')); + $container->setParameter('templating.loader.cache.path', $config['cache']); + } else { + $container->setParameter('templating.loader.cache.path', null); + } + + if (isset($config['cache_warmer'])) { + $container->getDefinition('templating.cache_warmer.template_paths')->addTag('kernel.cache_warmer'); + $container->setAlias('templating.locator', 'templating.locator.cached'); + } + + if (empty($config['engines'])) { + throw new \LogicException('You must register at least one templating engine.'); } $this->addClassesToCompile(array( - 'Symfony\\Component\\Routing\\RouterInterface', - 'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface', - 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', - 'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface', - 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', - $container->findDefinition('router')->getClass() + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface', + 'Symfony\\Component\\Templating\\EngineInterface', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocatorInterface', + $container->findDefinition('templating.locator')->getClass(), )); + + if (in_array('php', $config['engines'], true)) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Templating\\PhpEngine', + 'Symfony\\Component\\Templating\\TemplateNameParserInterface', + 'Symfony\\Component\\Templating\\TemplateNameParser', + 'Symfony\\Component\\Templating\\Loader\\LoaderInterface', + 'Symfony\\Component\\Templating\\Storage\\Storage', + 'Symfony\\Component\\Templating\\Storage\\FileStorage', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', + )); + } + + $engines = array_map(function($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']); + + // Use a deligation unless only a single engine was registered + if (1 === count($engines)) { + $container->setAlias('templating', (string) reset($engines)); + } else { + $container->getDefinition('templating.engine.delegating')->setArgument(1, $engines); + $container->setAlias('templating', 'templating.engine.delegating'); + } } /** - * Loads the profiler configuration. - * - * - * - * - * - * - * - * + * Loads the translator configuration. * - * @param array $config A configuration array + * @param array $config A translator configuration array * @param ContainerBuilder $container A ContainerBuilder instance */ - protected function registerProfilerConfiguration(array $config, ContainerBuilder $container) + private function registerTranslatorConfiguration(array $config, ContainerBuilder $container) { - if ($config['profiler']) { - if (!$container->hasDefinition('profiler')) { - $loader = new XmlFileLoader($container, __DIR__.'/../Resources/config'); - $loader->load('profiling.xml'); - $loader->load('collectors.xml'); - } + if (isset($config['enabled']) && $config['enabled']) { + // Use the "real" translator instead of the identity default + $container->setDefinition('translator', $container->findDefinition('translator.real')); - if (isset($config['profiler']['only-exceptions'])) { - $container->setParameter('profiler_listener.only_exceptions', $config['profiler']['only-exceptions']); - } elseif (isset($config['profiler']['only_exceptions'])) { - $container->setParameter('profiler_listener.only_exceptions', $config['profiler']['only_exceptions']); + // Discover translation directories + $dirs = array(); + foreach ($container->getParameter('kernel.bundles') as $bundle) { + $reflection = new \ReflectionClass($bundle); + if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) { + $dirs[] = $dir; + } + } + if (is_dir($dir = $container->getParameter('kernel.root_dir').'/translations')) { + $dirs[] = $dir; } - if (isset($config['profiler']['matcher'])) { - if (isset($config['profiler']['matcher']['service'])) { - $container->setAlias('profiler.request_matcher', $config['profiler']['matcher']['service']); - } elseif (isset($config['profiler']['matcher']['_services'])) { - $container->setAlias('profiler.request_matcher', (string) $config['profiler']['matcher']['_services'][0]); - } else { - $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher'); - $definition->setPublic(false); - - if (isset($config['profiler']['matcher']['ip'])) { - $definition->addMethodCall('matchIp', array($config['profiler']['matcher']['ip'])); - } - - if (isset($config['profiler']['matcher']['path'])) { - $definition->addMethodCall('matchPath', array($config['profiler']['matcher']['path'])); - } + // Register translation resources + $resources = array(); + if ($dirs) { + $finder = new Finder(); + $finder->files()->filter(function (\SplFileInfo $file) { return 2 === substr_count($file->getBasename(), '.'); })->in($dirs); + foreach ($finder as $file) { + // filename is domain.locale.format + list($domain, $locale, $format) = explode('.', $file->getBasename()); + + $resources[] = array($format, (string) $file, $locale, $domain); } - } else { - $container->removeAlias('profiler.request_matcher'); } - } elseif ($container->hasDefinition('profiler')) { - $container->getDefinition('profiling')->clearTags(); + $container->setParameter('translation.resources', $resources); + } + + if (isset($config['fallback'])) { + $container->setParameter('translator.fallback_locale', $config['fallback']); } } /** * Loads the validator configuration. * - * @param array $config A configuration array + * @param array $config A validation configuration array * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance */ - protected function registerValidationConfiguration(array $config, ContainerBuilder $container) + private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if ($config['validation']['enabled']) { - if (!$container->hasDefinition('validator')) { - $loader = new XmlFileLoader($container, __DIR__.'/../Resources/config'); - $loader->load('validator.xml'); - } + if (empty($config['enabled'])) { + return; + } - $xmlMappingFiles = array(); - $yamlMappingFiles = array(); + $loader->load('validator.xml'); - // default entries by the framework - $xmlMappingFiles[] = __DIR__.'/../../../Component/Form/Resources/config/validation.xml'; + $xmlMappingFiles = array(); + $yamlMappingFiles = array(); - foreach ($container->getParameter('kernel.bundles') as $bundle) { - $reflection = new \ReflectionClass($bundle); - if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.xml')) { - $xmlMappingFiles[] = realpath($file); - } - if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.yml')) { - $yamlMappingFiles[] = realpath($file); - } + // Include default entries from the framework + $xmlMappingFiles[] = __DIR__.'/../../../Component/Form/Resources/config/validation.xml'; + + foreach ($container->getParameter('kernel.bundles') as $bundle) { + $reflection = new \ReflectionClass($bundle); + if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.xml')) { + $xmlMappingFiles[] = realpath($file); } + if (file_exists($file = dirname($reflection->getFilename()).'/Resources/config/validation.yml')) { + $yamlMappingFiles[] = realpath($file); + } + } - $xmlFilesLoader = new Definition( - $container->getParameter('validator.mapping.loader.xml_files_loader.class'), - array($xmlMappingFiles) - ); - $xmlFilesLoader->setPublic(false); + $xmlFilesLoader = new Definition('%validator.mapping.loader.xml_files_loader.class%', array($xmlMappingFiles)); + $xmlFilesLoader->setPublic(false); - $yamlFilesLoader = new Definition( - $container->getParameter('validator.mapping.loader.yaml_files_loader.class'), - array($yamlMappingFiles) - ); - $yamlFilesLoader->setPublic(false); + $yamlFilesLoader = new Definition('%validator.mapping.loader.yaml_files_loader.class%', array($yamlMappingFiles)); + $yamlFilesLoader->setPublic(false); - $container->setDefinition('validator.mapping.loader.xml_files_loader', $xmlFilesLoader); - $container->setDefinition('validator.mapping.loader.yaml_files_loader', $yamlFilesLoader); + $container->setDefinition('validator.mapping.loader.xml_files_loader', $xmlFilesLoader); + $container->setDefinition('validator.mapping.loader.yaml_files_loader', $yamlFilesLoader); - foreach ($xmlMappingFiles as $file) { - $container->addResource(new FileResource($file)); - } + foreach ($xmlMappingFiles as $file) { + $container->addResource(new FileResource($file)); + } - foreach ($yamlMappingFiles as $file) { - $container->addResource(new FileResource($file)); - } + foreach ($yamlMappingFiles as $file) { + $container->addResource(new FileResource($file)); + } - if (isset($config['validation']['annotations'])) { - if (isset($config['validation']['annotations']['namespaces']) && is_array($config['validation']['annotations']['namespaces'])) { - $container->setParameter('validator.annotations.namespaces', array_merge( - $container->getParameter('validator.annotations.namespaces'), - $config['validation']['annotations']['namespaces'] - )); - } + if (isset($config['annotations'])) { + // Register prefixes for constraint namespaces + if (!empty($config['annotations']['namespaces'])) { + $container->setParameter('validator.annotations.namespaces', array_merge( + $container->getParameter('validator.annotations.namespaces'), + $config['annotations']['namespaces'] + )); + } - $annotationLoader = new Definition($container->getParameter('validator.mapping.loader.annotation_loader.class')); - $annotationLoader->setPublic(false); - $annotationLoader->addArgument(new Parameter('validator.annotations.namespaces')); + // Register annotation loader + $annotationLoader = new Definition('%validator.mapping.loader.annotation_loader.class%'); + $annotationLoader->setPublic(false); + $annotationLoader->addArgument(new Parameter('validator.annotations.namespaces')); - $container->setDefinition('validator.mapping.loader.annotation_loader', $annotationLoader); + $container->setDefinition('validator.mapping.loader.annotation_loader', $annotationLoader); - $loader = $container->getDefinition('validator.mapping.loader.loader_chain'); - $arguments = $loader->getArguments(); - array_unshift($arguments[0], new Reference('validator.mapping.loader.annotation_loader')); - $loader->setArguments($arguments); - } - } elseif ($container->hasDefinition('validator')) { - $container->getDefinition('validator')->clearTags(); + $loaderChain = $container->getDefinition('validator.mapping.loader.loader_chain'); + $arguments = $loaderChain->getArguments(); + array_unshift($arguments[0], new Reference('validator.mapping.loader.annotation_loader')); + $loaderChain->setArguments($arguments); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index fc15e0804ff83..c7a5577ef7a2e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -80,20 +80,4 @@ public function registerExtensions(ContainerBuilder $container) $container->addCompilerPass(new TranslatorPass()); $container->addCompilerPass(new AddCacheWarmerPass()); } - - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 65ff4e29b029c..c75ca9aa9df1a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -22,6 +22,7 @@ Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer %kernel.name%%kernel.environment%UrlMatcher %kernel.name%%kernel.environment%UrlGenerator + @@ -71,6 +72,7 @@ %router.options.matcher_base_class% %router.options.matcher_dumper_class% %router.options.matcher.cache_class% + %router.options.resource_type% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index a70f035342b6d..4a635a758e83a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -9,34 +9,51 @@ - - + + + - - + - + - + + - + + + + + + + + + + + + + + + + + @@ -46,14 +63,9 @@ + - - - - - - @@ -64,11 +76,14 @@ - - + + + + + @@ -83,21 +98,26 @@ - - + + - + - - + - + + + + + + - - + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/skeleton/bundle/Bundle.php b/src/Symfony/Bundle/FrameworkBundle/Resources/skeleton/bundle/Bundle.php index 2c101de81610a..7721df5248599 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/skeleton/bundle/Bundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/skeleton/bundle/Bundle.php @@ -6,19 +6,4 @@ class {{ bundle }} extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return strtr(__DIR__, '\\', '/'); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.es.xliff b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.es.xliff new file mode 100644 index 0000000000000..96e2fc6063498 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.es.xliff @@ -0,0 +1,131 @@ + + + + + + This value should be false + Este valor debería ser falso + + + This value should be true + Este valor debería ser verdadero + + + This value should be of type {{ type }} + Este valor debería ser de tipo {{ type }} + + + This value should be blank + Este valor debería estar vacío + + + This value should be one of the given choices + Seleccione este valor entre las opciones mostradas + + + You should select at least {{ limit }} choices + Debería seleccionar al menos {{ limit }} opciones + + + You should select at most {{ limit }} choices + Debería seleccionar como máximo {{ limit }} opciones + + + The fields {{ fields }} were not expected + No se esperaban los campos {{ fields }} + + + The fields {{ fields }} are missing + Faltan los campos {{ fields }} + + + This value is not a valid date + Este valor no es una fecha válida + + + This value is not a valid datetime + Este valor no es una fecha y hora válidas + + + This value is not a valid email address + Este valor no es una dirección de email válida + + + The file could not be found + No se pudo encontrar el archivo + + + The file is not readable + No se puede leer el archivo + + + The file is too large ({{ size }}). Allowed maximum size is {{ limit }} + El archivo es demasiado grande ({{ size }}). El tamaño máximo permitido es {{ limit }} + + + The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }} + El tipo mime del archivo no es válido ({{ type }}). Los tipos mime válidos son {{ types }} + + + This value should be {{ limit }} or less + Este valor debería ser {{ limit }} o menos + + + This value is too long. It should have {{ limit }} characters or less + Este valor es demasiado largo. Debería tener {{ limit }} caracteres o menos + + + This value should be {{ limit }} or more + Este valor debería ser {{ limit }} o más + + + This value is too short. It should have {{ limit }} characters or more + Este valor es demasiado corto. Debería tener {{ limit }} caracteres o más + + + This value should not be blank + Este valor no debería estar vacío + + + This value should not be null + Este valor no debería ser null + + + This value should be null + Este valor debería ser null + + + This value is not valid + Este valor no es válido + + + This value is not a valid time + Este valor no es una hora válida + + + This value is not a valid URL + Este valor no es una URL válida + + + This value should be instance of class {{ class }} + Este valor debería ser una instancia de la clase {{ class }} + + + This field group should not contain extra fields + Este grupo de campos no debería contener campos adicionales + + + The uploaded file was too large. Please try to upload a smaller file + El archivo subido es demasiado grande. Por favor, suba un archivo más pequeño + + + The CSRF token is invalid + El token CSRF no es válido + + + The two values should be equal + Los dos valores deberían ser iguales + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.ja_JP.xliff b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.ja_JP.xliff new file mode 100644 index 0000000000000..cabba0c0d2f69 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.ja_JP.xliff @@ -0,0 +1,131 @@ + + + + + + This value should be false + 値はfalseでなければなりません + + + This value should be true + 値はtrueでなければなりません + + + This value should be of type {{ type }} + 値の型は{{ type }}でなければなりません + + + This value should be blank + 値は空でなければなりません + + + This value should be one of the given choices + 値は指定された選択肢のうちの1つでなければなりません + + + You should select at least {{ limit }} choices + {{ limit }}個以上選択してください + + + You should select at most {{ limit }} choices + {{ limit }}個以内で選択してください + + + The fields {{ fields }} were not expected + フィールド{{ fields }}は無効です + + + The fields {{ fields }} are missing + フィールド{{ fields }}は必須です + + + This value is not a valid date + 値が有効な日付ではありません + + + This value is not a valid datetime + 値が有効な日時ではありません + + + This value is not a valid email address + 値が有効なメールアドレスではありません + + + The file could not be found + ファイルが見つかりません + + + The file is not readable + ファイルを読み込めません + + + The file is too large ({{ size }}). Allowed maximum size is {{ limit }} + ファイルのサイズが大きすぎます({{ size }})。有効な最大サイズは{{ limit }}です + + + The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }} + ファイルのMIMEタイプが無効です({{ type }})。有効なMIMEタイプは{{ types }}です + + + This value should be {{ limit }} or less + 値は{{ limit }}以下でなければなりません + + + This value is too long. It should have {{ limit }} characters or less + 値が長すぎます。{{ limit }}文字以内でなければなりません + + + This value should be {{ limit }} or more + 値は{{ limit }}以上でなければなりません + + + This value is too short. It should have {{ limit }} characters or more + 値が短すぎます。{{ limit }}文字以上でなければなりません + + + This value should not be blank + 値が空であってはなりません + + + This value should not be null + 値がnullであってはなりません + + + This value should be null + 値はnullでなければなりません + + + This value is not valid + 値が無効です + + + This value is not a valid time + 値が有効な時刻ではありません + + + This value is not a valid URL + 値が有効なURLではありません + + + This value should be instance of class {{ class }} + 値は{{ class }}のインスタンスでなければなりません + + + This field group should not contain extra fields + フィールドグループに追加のフィールドを含んではなりません + + + The uploaded file was too large. Please try to upload a smaller file + アップロードされたファイルが大きすぎます。小さなファイルで再度アップロードしてください + + + The CSRF token is invalid + CSRFトークンが無効です + + + The two values should be equal + 2つの値が同じでなければなりません + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_field.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_field.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_field.php rename to src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_field.html.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php b/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php index 438d32ee18d1a..8885834462922 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php @@ -69,7 +69,7 @@ protected function getEngine($name) } } - throw new \RuntimeException(sprintf('No engine is able to work with the "%s" template.', $name)); + throw new \RuntimeException(sprintf('No engine is able to work with the %s template.', json_encode($name))); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php index ff762c31070d0..e6ecf2a3cafe6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php @@ -53,12 +53,12 @@ public function parse($name) $parts = explode(':', $name); if (3 !== count($parts)) { - throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.engine.format").', $name)); + throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.format.engine").', $name)); } $elements = explode('.', $parts[2]); if (3 !== count($elements)) { - throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.engine.format").', $name)); + throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid (format is "bundle:section:template.format.engine").', $name)); } $parameters = array( diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php new file mode 100644 index 0000000000000..5f0d3a06a8d13 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -0,0 +1,122 @@ + + * + * 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\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Bundle\FrameworkBundle\Tests\Logger; +use Symfony\Bundle\FrameworkBundle\Tests\Kernel; + + + +/** + * + * @author Marcin Sikon + */ +class RedirectControllerTest extends TestCase +{ + public function testEmptyRoute() + { + $response = new Response(); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('get') + ->with($this->equalTo('response')) + ->will($this->returnValue($response)) + ; + + $controller = new RedirectController(); + $controller->setContainer($container); + + $returnResponse = $controller->redirectAction(''); + + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse); + + $this->assertEquals(410, $returnResponse->getStatusCode()); + } + + + + /** + * @dataProvider provider + */ + public function testRoute($permanent, $expectedCode) + { + $response = new Response(); + $request = new Request(); + + $route = 'new-route'; + $url = '/redirect-url'; + $params = array('additional-parameter' => 'value'); + + + $request->attributes = new ParameterBag(array('route' => $route, '_route' => 'current-route', 'permanent' => $permanent) + $params); + + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->once()) + ->method('generate') + ->with($this->equalTo($route),$this->equalTo($params)) + ->will($this->returnValue($url)); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + + $container + ->expects($this->at(0)) + ->method('get') + ->with($this->equalTo('request')) + ->will($this->returnValue($request)); + + $container + ->expects($this->at(1)) + ->method('get') + ->with($this->equalTo('response')) + ->will($this->returnValue($response)); + + $container + ->expects($this->at(2)) + ->method('get') + ->with($this->equalTo('router')) + ->will($this->returnValue($router)); + + + $controller = new RedirectController(); + $controller->setContainer($container); + + $returnResponse = $controller->redirectAction($route, $permanent); + + + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse); + + $this->assertTrue($returnResponse->isRedirect()); + $this->assertTrue($returnResponse->isRedirected($url)); + $this->assertEquals($expectedCode, $returnResponse->getStatusCode()); + } + + + + public function provider() + { + return array( + array(true, 301), + array(false, 302), + ); + } + +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php new file mode 100644 index 0000000000000..7439b4dc9f972 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -0,0 +1,46 @@ +loadFromExtension('app', 'config', array( + 'csrf_protection' => array( + 'enabled' => true, + 'field_name' => '_csrf', + 'secret' => 's3cr3t', + ), + 'esi' => array( + 'enabled' => true, + ), + 'profiler' => array( + 'only_exceptions' => true, + ), + 'router' => array( + 'cache_warmer' => true, + 'resource' => '%kernel.root_dir%/config/routing.xml', + 'type' => 'xml', + ), + 'session' => array( + 'auto_start' => true, + 'class' => 'Session', + 'default_locale' => 'fr', + 'storage_id' => 'native', + 'name' => '_SYMFONY', + 'lifetime' => 86400, + 'path' => '/', + 'domain' => 'example.com', + 'secure' => true, + 'httponly' => true, + ), + 'templating' => array( + 'assets_version' => 'SomeVersionScheme', + 'assets_base_urls' => 'http://cdn.example.com', + 'cache_warmer' => true, + 'engines' => array('php', 'twig'), + 'loader' => array('loader.foo', 'loader.bar'), + ), + 'translator' => array( + 'enabled' => true, + 'fallback' => 'fr', + ), + 'validation' => array( + 'enabled' => true, + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/session_pdo.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/session_pdo.php new file mode 100644 index 0000000000000..eb8deb9d1f01f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/session_pdo.php @@ -0,0 +1,11 @@ +loadFromExtension('app', 'config', array( + 'session' => array( + 'storage_id' => 'pdo', + 'pdo.db_table' => 'table', + 'pdo.db_id_col' => 'id', + 'pdo.db_data_col' => 'data', + 'pdo.db_time_col' => 'time', + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php new file mode 100644 index 0000000000000..bed6c991b44d2 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php @@ -0,0 +1,12 @@ +loadFromExtension('app', 'config', array( + 'validation' => array( + 'enabled' => true, + 'annotations' => array( + 'namespaces' => array( + 'app' => 'Application\\Validator\\Constraints\\', + ), + ), + ), +)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml new file mode 100644 index 0000000000000..8c508d98e2c38 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + loader.foo + loader.bar + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/session_pdo.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/session_pdo.xml new file mode 100644 index 0000000000000..84455afff6d0c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/session_pdo.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml new file mode 100644 index 0000000000000..1af18e148f7f2 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml new file mode 100644 index 0000000000000..74571b6e6d479 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -0,0 +1,35 @@ +app.config: + csrf_protection: + enabled: true + field_name: _csrf + secret: s3cr3t + esi: + enabled: true + profiler: + only_exceptions: true + router: + cache_warmer: true + resource: %kernel.root_dir%/config/routing.xml + type: xml + session: + auto_start: true + class: Session + default_locale: fr + storage_id: native + name: _SYMFONY + lifetime: 86400 + path: / + domain: example.com + secure: true + httponly: true + templating: + assets_version: SomeVersionScheme + assets_base_urls: http://cdn.example.com + cache_warmer: true + engines: [php, twig] + loader: [loader.foo, loader.bar] + translator: + enabled: true + fallback: fr + validation: + enabled: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/session_pdo.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/session_pdo.yml new file mode 100644 index 0000000000000..06323d53de9b4 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/session_pdo.yml @@ -0,0 +1,7 @@ +app.config: + session: + storage_id: pdo + pdo.db_table: table + pdo.db_id_col: id + pdo.db_data_col: data + pdo.db_time_col: time diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml new file mode 100644 index 0000000000000..1ff2545facb94 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml @@ -0,0 +1,6 @@ +app.config: + validation: + enabled: true + annotations: + namespaces: + app: Application\Validator\Constraints\ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 0e83eb1ff8169..95d7d21decfc8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -16,43 +16,183 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; -class FrameworkExtensionTest extends TestCase +abstract class FrameworkExtensionTest extends TestCase { - public function testConfigLoad() + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testCsrfProtection() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->getParameter('form.csrf_protection.enabled')); + $this->assertEquals('_csrf', $container->getParameter('form.csrf_protection.field_name')); + $this->assertEquals('s3cr3t', $container->getParameter('form.csrf_protection.secret')); + } + + public function testEsi() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('esi'), '->registerEsiConfiguration() loads esi.xml'); + } + + public function testProfiler() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('profiler'), '->registerProfilerConfiguration() loads profiling.xml'); + $this->assertTrue($container->hasDefinition('data_collector.config'), '->registerProfilerConfiguration() loads collectors.xml'); + $this->assertTrue($container->getParameter('profiler_listener.only_exceptions')); + } + + public function testRouter() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('router.real'), '->registerRouterConfiguration() loads routing.xml'); + $this->assertEquals($container->getParameter('kernel.root_dir').'/config/routing.xml', $container->getParameter('routing.resource'), '->registerRouterConfiguration() sets routing resource'); + $this->assertEquals('xml', $container->getParameter('router.options.resource_type'), '->registerRouterConfiguration() sets routing resource type'); + $this->assertTrue($container->getDefinition('router.cache_warmer')->hasTag('kernel.cache_warmer'), '->registerRouterConfiguration() tags router cache warmer if cache warming is set'); + $this->assertEquals('router.cached', (string) $container->getAlias('router'), '->registerRouterConfiguration() changes router alias to cached if cache warming is set'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testRouterRequiresResourceOption() { - $container = $this->getContainer(); + $container = $this->createContainer(); $loader = new FrameworkExtension(); + $loader->configLoad(array(array('router' => true)), $container); + } + + public function testSession() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml'); + $this->assertEquals('fr', $container->getParameter('session.default_locale')); + $this->assertTrue($container->getDefinition('session')->hasMethodCall('start')); + $this->assertEquals('Session', $container->getParameter('session.class')); + $this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage')); + + $options = $container->getParameter('session.storage.native.options'); + $this->assertEquals('_SYMFONY', $options['name']); + $this->assertEquals(86400, $options['lifetime']); + $this->assertEquals('/', $options['path']); + $this->assertEquals('example.com', $options['domain']); + $this->assertTrue($options['secure']); + $this->assertTrue($options['httponly']); + } + + public function testSessionPdo() + { + $container = $this->createContainerFromFile('session_pdo'); + $options = $container->getParameter('session.storage.pdo.options'); + + $this->assertEquals('session.storage.pdo', (string) $container->getAlias('session.storage')); + $this->assertEquals('table', $options['db_table']); + $this->assertEquals('id', $options['db_id_col']); + $this->assertEquals('data', $options['db_data_col']); + $this->assertEquals('time', $options['db_time_col']); + } + + public function testTemplating() + { + $container = $this->createContainerFromFile('full'); - $loader->configLoad(array(array()), $container); - $this->assertEquals('Symfony\\Bundle\\FrameworkBundle\\RequestListener', $container->getParameter('request_listener.class'), '->webLoad() loads the web.xml file if not already loaded'); + $this->assertTrue($container->hasDefinition('templating.name_parser'), '->registerTemplatingConfiguration() loads templating.xml'); + $this->assertEquals('SomeVersionScheme', $container->getParameter('templating.assets.version')); + $this->assertEquals('http://cdn.example.com', $container->getParameter('templating.assets.base_urls')); - $container = $this->getContainer(); + $this->assertTrue($container->getDefinition('templating.cache_warmer.template_paths')->hasTag('kernel.cache_warmer'), '->registerTemplatingConfiguration() tags templating cache warmer if cache warming is set'); + $this->assertEquals('templating.locator.cached', (string) $container->getAlias('templating.locator'), '->registerTemplatingConfiguration() changes templating.locator alias to cached if cache warming is set'); + + $this->assertEquals('templating.engine.delegating', (string) $container->getAlias('templating'), '->registerTemplatingConfiguration() configures delegating loader if multiple engines are provided'); + + $this->assertEquals('templating.loader.chain', (string) $container->getAlias('templating.loader'), '->registerTemplatingConfiguration() configures loader chain if multiple loaders are provided'); + } + + public function testTranslator() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('translator.real'), '->registerTranslatorConfiguration() loads translation.xml'); + $this->assertSame($container->getDefinition('translator.real'), $container->getDefinition('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator'); + + $this->assertContains( + realpath(__DIR__.'/../../Resources/translations/validators.fr.xliff'), + array_map(function($resource) { return $resource[1]; }, $container->getParameter('translation.resources')), + '->registerTranslatorConfiguration() finds FrameworkExtension translation resources' + ); + + $this->assertEquals('fr', $container->getParameter('translator.fallback_locale')); + } + + /** + * @expectedException LogicException + */ + public function testTemplatingRequiresAtLeastOneEngine() + { + $container = $this->createContainer(); $loader = new FrameworkExtension(); + $loader->configLoad(array(array('templating' => null)), $container); + } + + public function testValidation() + { + $container = $this->createContainerFromFile('full'); - // profiler - $loader->configLoad(array(array('profiler' => true)), $container); - $this->assertEquals('Symfony\Component\HttpKernel\Profiler\Profiler', $container->getParameter('profiler.class'), '->configLoad() loads the collectors.xml file if not already loaded'); + $this->assertTrue($container->hasDefinition('validator'), '->registerValidationConfiguration() loads validator.xml'); + $this->assertTrue($container->hasDefinition('validator.mapping.loader.xml_files_loader'), '->registerValidationConfiguration() defines the XML loader'); + $this->assertTrue($container->hasDefinition('validator.mapping.loader.yaml_files_loader'), '->registerValidationConfiguration() defines the YAML loader'); - // templating - $loader->configLoad(array(array('templating' => array('engines' => array('php')))), $container); - $this->assertEquals('Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', $container->getParameter('templating.engine.php.class'), '->templatingLoad() loads the templating.xml file if not already loaded'); + $xmlLoaderArgs = $container->getDefinition('validator.mapping.loader.xml_files_loader')->getArguments(); + $xmlFiles = $xmlLoaderArgs[0]; - // validation - $loader->configLoad(array(array('validation' => array('enabled' => true))), $container); - $this->assertEquals('Symfony\Component\Validator\Validator', $container->getParameter('validator.class'), '->validationLoad() loads the validation.xml file if not already loaded'); - $this->assertFalse($container->hasDefinition('validator.mapping.loader.annotation_loader'), '->validationLoad() doesn\'t load the annotations service unless its needed'); + $this->assertContains( + realpath(__DIR__.'/../../../../Component/Form/Resources/config/validation.xml'), + array_map('realpath', $xmlFiles), + '->registerValidationConfiguration() adds Form validation.xml to XML loader' + ); - $loader->configLoad(array(array('validation' => array('enabled' => true, 'annotations' => true))), $container); - $this->assertTrue($container->hasDefinition('validator.mapping.loader.annotation_loader'), '->validationLoad() loads the annotations service'); + $this->assertFalse($container->hasDefinition('validator.mapping.loader.annotation_loader'), '->registerValidationConfiguration() does not define the annotation loader unless needed'); } - protected function getContainer() + public function testValidationAnnotations() + { + $container = $this->createContainerFromFile('validation_annotations'); + + $this->assertTrue($container->hasDefinition('validator.mapping.loader.annotation_loader'), '->registerValidationConfiguration() defines the annotation loader'); + + $namespaces = $container->getParameter('validator.annotations.namespaces'); + $this->assertEquals('Symfony\\Component\\Validator\\Constraints\\', $namespaces['validation'], '->registerValidationConfiguration() loads the default "validation" namespace'); + $this->assertEquals('Application\\Validator\\Constraints\\', $namespaces['app'], '->registerValidationConfiguration() loads custom validation namespaces'); + } + + protected function createContainer() { return new ContainerBuilder(new ParameterBag(array( 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), - 'kernel.root_dir' => __DIR__, - 'kernel.debug' => false, + 'kernel.cache_dir' => __DIR__, 'kernel.compiled_classes' => array(), + 'kernel.debug' => false, + 'kernel.environment' => 'test', + 'kernel.name' => 'kernel', + 'kernel.root_dir' => __DIR__, ))); } + + protected function createContainerFromFile($file) + { + $container = $this->createContainer(); + $container->registerExtension(new FrameworkExtension()); + $this->loadFromFile($container, $file); + + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + + return $container; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php new file mode 100644 index 0000000000000..451d16364a329 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +class PhpFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new PhpFileLoader($container, __DIR__.'/Fixtures/php'); + $loader->load($file.'.php'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php new file mode 100644 index 0000000000000..ea6fbe08a7335 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +class XmlFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new XmlFileLoader($container, __DIR__.'/Fixtures/xml'); + $loader->load($file.'.xml'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php new file mode 100644 index 0000000000000..c1af39109e980 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + +class YamlFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new YamlFileLoader($container, __DIR__.'/Fixtures/yml'); + $loader->load($file.'.yml'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php index 1342b9b5eed32..22b95430711fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php @@ -27,20 +27,4 @@ public function getParent() { return 'SensioFooBundle'; } - - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php index 98dd4342273a2..af57d44bcdd2f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php @@ -20,19 +20,4 @@ */ class FooBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php index 811ed2f14115a..9e6918d34e955 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php @@ -20,19 +20,4 @@ */ class SensioCmsFooBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php index 2b547f923ece1..e8930625f3b19 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php @@ -20,19 +20,4 @@ */ class SensioFooBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000000000..2bb11266c2901 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php @@ -0,0 +1,235 @@ + + */ +class Configuration +{ + public function getAclConfigTree() + { + $tb = new TreeBuilder(); + + return $tb + ->root('security:acl', 'array') + ->scalarNode('connection')->end() + ->scalarNode('cache')->end() + ->end() + ->buildTree(); + } + + public function getFactoryConfigTree() + { + $tb = new TreeBuilder(); + + return $tb + ->root('security:config', 'array') + ->fixXmlConfig('factory', 'factories') + ->arrayNode('factories') + ->prototype('scalar')->end() + ->end() + ->end() + ->buildTree(); + } + + public function getMainConfigTree(array $factories) + { + $tb = new TreeBuilder(); + $rootNode = $tb->root('security:config', 'array'); + + $rootNode + ->scalarNode('access_denied_url')->end() + ->scalarNode('session_fixation_strategy')->cannotBeEmpty()->defaultValue('migrate')->end() + ; + + $this->addEncodersSection($rootNode); + $this->addProvidersSection($rootNode); + $this->addFirewallsSection($rootNode, $factories); + $this->addAccessControlSection($rootNode); + $this->addRoleHierarchySection($rootNode); + + return $tb->buildTree(); + } + + protected function addRoleHierarchySection($rootNode) + { + $rootNode + ->fixXmlConfig('role', 'role_hierarchy') + ->arrayNode('role_hierarchy') + ->containsNameValuePairsWithKeyAttribute('id') + ->prototype('array') + ->performNoDeepMerging() + ->beforeNormalization()->ifString()->then(function($v) { return array('value' => $v); })->end() + ->beforeNormalization() + ->ifTrue(function($v) { return is_array($v) && isset($v['value']); }) + ->then(function($v) { return preg_split('/\s*,\s*/', $v['value']); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ; + } + + protected function addAccessControlSection($rootNode) + { + $rootNode + ->fixXmlConfig('rule', 'access_control') + ->arrayNode('access_control') + ->cannotBeOverwritten() + ->prototype('array') + ->scalarNode('requires_channel')->defaultNull()->end() + ->scalarNode('path')->defaultNull()->end() + ->scalarNode('host')->defaultNull()->end() + ->scalarNode('ip')->defaultNull()->end() + ->arrayNode('methods') + ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->fixXmlConfig('role') + ->arrayNode('roles') + ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->fixXmlConfig('attribute') + ->arrayNode('attributes') + ->containsNameValuePairsWithKeyAttribute('key') + ->prototype('scalar') + ->beforeNormalization() + ->ifTrue(function($v) { return is_array($v) && isset($v['pattern']); }) + ->then(function($v) { return $v['pattern']; }) + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + protected function addFirewallsSection($rootNode, array $factories) + { + $firewallNodeBuilder = + $rootNode + ->fixXmlConfig('firewall') + ->arrayNode('firewalls') + ->disallowNewKeysInSubsequentConfigs() + ->useAttributeAsKey('name') + ->prototype('array') + ->scalarNode('pattern')->end() + ->booleanNode('security')->defaultTrue()->end() + ->scalarNode('request_matcher')->end() + ->scalarNode('access_denied_url')->end() + ->scalarNode('access_denied_handler')->end() + ->scalarNode('entry_point')->end() + ->scalarNode('provider')->end() + ->booleanNode('stateless')->defaultFalse()->end() + ->scalarNode('context')->cannotBeEmpty()->end() + ->arrayNode('logout') + ->treatTrueLike(array()) + ->canBeUnset() + ->scalarNode('path')->defaultValue('/logout')->end() + ->scalarNode('target')->defaultValue('/')->end() + ->booleanNode('invalidate_session')->defaultTrue()->end() + ->fixXmlConfig('delete_cookie') + ->arrayNode('delete_cookies') + ->beforeNormalization() + ->ifTrue(function($v) { return is_array($v) && is_int(key($v)); }) + ->then(function($v) { return array_map(function($v) { return array('name' => $v); }, $v); }) + ->end() + ->useAttributeAsKey('name') + ->prototype('array') + ->scalarNode('path')->defaultNull()->end() + ->scalarNode('domain')->defaultNull()->end() + ->end() + ->end() + ->fixXmlConfig('handler') + ->arrayNode('handlers') + ->prototype('scalar')->end() + ->end() + ->end() + ->booleanNode('anonymous')->end() + ->arrayNode('switch_user') + ->scalarNode('provider')->end() + ->scalarNode('parameter')->defaultValue('_switch_user')->end() + ->scalarNode('role')->defaultValue('ROLE_ALLOWED_TO_SWITCH')->end() + ->end() + ; + + foreach ($factories as $factoriesAtPosition) { + foreach ($factoriesAtPosition as $factory) { + $factoryNode = + $firewallNodeBuilder->arrayNode(str_replace('-', '_', $factory->getKey())) + ->canBeUnset() + ; + + $factory->addConfiguration($factoryNode); + } + } + } + + protected function addProvidersSection($rootNode) + { + $rootNode + ->fixXmlConfig('provider') + ->arrayNode('providers') + ->disallowNewKeysInSubsequentConfigs() + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ->scalarNode('id')->end() + ->fixXmlConfig('provider') + ->arrayNode('providers') + ->prototype('scalar')->end() + ->end() + ->fixXmlConfig('user') + ->arrayNode('users') + ->useAttributeAsKey('name') + ->prototype('array') + ->scalarNode('password')->defaultValue(uniqid())->end() + ->arrayNode('roles') + ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->arrayNode('entity') + ->scalarNode('class')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('property')->defaultNull()->end() + ->end() + ->arrayNode('document') + ->scalarNode('class')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('property')->defaultNull()->end() + ->end() + ->end() + ->end() + ; + } + + protected function addEncodersSection($rootNode) + { + $rootNode + ->fixXmlConfig('encoder') + ->arrayNode('encoders') + ->useAttributeAsKey('class') + ->prototype('array') + ->beforeNormalization()->ifString()->then(function($v) { return array('algorithm' => $v); })->end() + ->scalarNode('algorithm')->isRequired()->cannotBeEmpty()->end() + ->booleanNode('ignore_case')->end() + ->booleanNode('encode_as_base64')->end() + ->scalarNode('iterations')->end() + ->scalarNode('id')->end() + ->end() + ->end() + ; + } +} \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index 860ba9e145b99..88b481e3b1f16 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; + use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -38,10 +40,6 @@ abstract class AbstractFactory implements SecurityFactoryInterface public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId) { - if (!is_array($config)) { - $config = array(); - } - // authentication provider $authProviderId = $this->createAuthProvider($container, $id, $config, $userProviderId); $container @@ -66,6 +64,24 @@ public function create(ContainerBuilder $container, $id, $config, $userProviderI return array($authProviderId, $listenerId, $entryPointId); } + public function addConfiguration(NodeBuilder $node) + { + $node + ->scalarNode('provider')->end() + ->booleanNode('remember_me')->defaultTrue()->end() + ->scalarNode('success_handler')->end() + ->scalarNode('failure_handler')->end() + ; + + foreach ($this->options as $name => $default) { + if (is_bool($default)) { + $node->booleanNode($name)->defaultValue($default); + } else { + $node->scalarNode($name)->defaultValue($default); + } + } + } + public final function addOption($name, $default = null) { $this->options[$name] = $default; @@ -127,18 +143,15 @@ protected function createEntryPoint($container, $id, $config, $defaultEntryPoint */ protected function isRememberMeAware($config) { - return !isset($config['remember_me']) || (Boolean) $config['remember_me']; + return $config['remember_me']; } protected function createListener($container, $id, $config, $userProvider) { - // merge set options with default options - $options = $this->getOptionsFromConfig($config); - $listenerId = $this->getListenerId(); $listener = new DefinitionDecorator($listenerId); $listener->setArgument(3, $id); - $listener->setArgument(4, $options); + $listener->setArgument(4, array_intersect_key($config, $this->options)); // success handler if (isset($config['success_handler'])) { @@ -155,17 +168,4 @@ protected function createListener($container, $id, $config, $userProvider) return $listenerId; } - - protected final function getOptionsFromConfig($config) - { - $options = $this->options; - - foreach (array_keys($options) as $key) { - if (array_key_exists($key, $config)) { - $options[$key] = $config[$key]; - } - } - - return $options; - } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php index 6149361fb8871..eef9aecdc4251 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php @@ -59,14 +59,11 @@ protected function createAuthProvider(ContainerBuilder $container, $id, $config, protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) { - // merge set options with default options - $options = $this->getOptionsFromConfig($config); - $entryPointId = 'security.authentication.form_entry_point.'.$id; $container ->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point')) - ->addArgument($options['login_path']) - ->addArgument($options['use_forward']) + ->addArgument($config['login_path']) + ->addArgument($config['use_forward']) ; return $entryPointId; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php index 3686a1e06f1d2..9b2b7a870a864 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; + use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -53,4 +55,11 @@ public function getKey() { return 'http-basic'; } + + public function addConfiguration(NodeBuilder $builder) + { + $builder + ->scalarNode('provider')->end() + ; + } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php index d837032e41096..49cf748cf40e9 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; + use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -53,4 +55,11 @@ public function getKey() { return 'http-digest'; } + + public function addConfiguration(NodeBuilder $builder) + { + $builder + ->scalarNode('provider')->end() + ; + } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index e8646e90d09a6..c4727429ab5e3 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -2,6 +2,8 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; + use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Reference; @@ -10,16 +12,19 @@ class RememberMeFactory implements SecurityFactoryInterface { + protected $options = array( + 'name' => 'REMEMBERME', + 'lifetime' => 31536000, + 'path' => '/', + 'domain' => null, + 'secure' => false, + 'httponly' => true, + 'always_remember_me' => false, + 'remember_me_parameter' => '_remember_me', + ); + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) { - if (!isset($config['key']) || empty($config['key'])) { - throw new \RuntimeException('A "key" must be defined for each remember-me section.'); - } - - if (isset($config['provider'])) { - throw new \RuntimeException('You must not set a user provider for remember-me.'); - } - // authentication provider $authProviderId = 'security.authentication.provider.rememberme.'.$id; $container @@ -60,22 +65,7 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, } // remember-me options - $options = array( - 'name' => 'REMEMBERME', - 'lifetime' => 31536000, - 'path' => '/', - 'domain' => null, - 'secure' => false, - 'httponly' => true, - 'always_remember_me' => false, - 'remember_me_parameter' => '_remember_me', - ); - foreach ($options as $name => $option) { - if (array_key_exists($name, $config)) { - $options[$name] = $config[$name]; - } - } - $rememberMeServices->setArgument(3, $options); + $rememberMeServices->setArgument(3, array_intersect_key($config, $this->options)); // attach to remember-me aware listeners $userProviders = array(); @@ -118,4 +108,20 @@ public function getKey() { return 'remember-me'; } + + public function addConfiguration(NodeBuilder $node) + { + $node + ->scalarNode('key')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('token_provider')->end() + ; + + foreach ($this->options as $name => $value) { + if (is_bool($value)) { + $node->booleanNode($name)->defaultValue($value); + } else { + $node->scalarNode($name)->defaultValue($value); + } + } + } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php index a2ec07df051ce..05dcc74f8a4c8 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -25,4 +26,6 @@ function create(ContainerBuilder $container, $id, $config, $userProvider, $defau function getPosition(); function getKey(); + + function addConfiguration(NodeBuilder $builder); } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php index dbf0e359148ac..d53f75d978ca8 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; +use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder; + use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -50,4 +52,11 @@ public function getKey() { return 'x509'; } + + public function addConfiguration(NodeBuilder $builder) + { + $builder + ->scalarNode('provider')->end() + ; + } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index edc27e69f21da..ac835082bd1a8 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection; +use Symfony\Component\DependencyInjection\Configuration\Processor; +use Symfony\Component\DependencyInjection\Configuration\Builder\TreeBuilder; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -34,44 +36,38 @@ class SecurityExtension extends Extension protected $requestMatchers = array(); protected $contextListeners = array(); protected $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me'); + protected $configuration; + protected $factories; - public function configLoad(array $configs, ContainerBuilder $container) + public function __construct() { - foreach ($configs as $config) { - $this->doConfigLoad($this->normalizeKeys($config), $container); - } + $this->configuration = new Configuration(); } - public function aclLoad(array $configs, ContainerBuilder $container) + public function configLoad(array $configs, ContainerBuilder $container) { - foreach ($configs as $config) { - $this->doAclLoad($this->normalizeKeys($config), $container); - } - } + $processor = new Processor(); - /** - * Loads the web configuration. - * - * @param array $config An array of configuration settings - * @param ContainerBuilder $container A ContainerBuilder instance - */ - protected function doConfigLoad($config, ContainerBuilder $container) - { - if (!$container->hasDefinition('security.context')) { - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('security.xml'); - $loader->load('security_listeners.xml'); - $loader->load('security_rememberme.xml'); - $loader->load('templating_php.xml'); - $loader->load('templating_twig.xml'); - $loader->load('collectors.xml'); - } + // first assemble the factories + $factories = $this->createListenerFactories($container, $processor->process($this->configuration->getFactoryConfigTree(), $configs)); + + // normalize and merge the actual configuration + $tree = $this->configuration->getMainConfigTree($factories); + $config = $processor->process($tree, $configs); + // load services + $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); + $loader->load('security.xml'); + $loader->load('security_listeners.xml'); + $loader->load('security_rememberme.xml'); + $loader->load('templating_php.xml'); + $loader->load('templating_twig.xml'); + $loader->load('collectors.xml'); + + // set some global scalars if (isset($config['access_denied_url'])) { $container->setParameter('security.access.denied_url', $config['access_denied_url']); } - - // session fixation protection if (isset($config['session_fixation_protection'])) { $container->setParameter('security.authentication.session_strategy.strategy', $config['session_fixation_protection']); } @@ -79,97 +75,95 @@ protected function doConfigLoad($config, ContainerBuilder $container) $this->createFirewalls($config, $container); $this->createAuthorization($config, $container); $this->createRoleHierarchy($config, $container); - - return $container; } - protected function createRoleHierarchy($config, ContainerBuilder $container) + public function aclLoad(array $configs, ContainerBuilder $container) { - $roles = array(); - if (isset($config['role_hierarchy'])) { - $roles = $config['role_hierarchy']; + $processor = new Processor(); + $config = $processor->process($this->configuration->getAclConfigTree(), $configs); + + $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); + $loader->load('security_acl.xml'); + + if (isset($config['connection'])) { + $container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection'])); } - if (isset($roles['role']) && is_int(key($roles['role']))) { - $roles = $roles['role']; + if (isset($config['cache'])) { + $container->setAlias('security.acl.cache', sprintf('security.acl.cache.%s', $config['cache'])); } + } - $hierarchy = array(); - foreach ($roles as $id => $role) { - if (is_array($role) && isset($role['id'])) { - $id = $role['id']; - } + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } - $value = $role; - if (is_array($role) && isset($role['value'])) { - $value = $role['value']; - } + public function getNamespace() + { + return 'http://www.symfony-project.org/schema/dic/security'; + } + + public function getAlias() + { + return 'security'; + } + + /** + * Loads the web configuration. + * + * @param array $config An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ - $hierarchy[$id] = is_array($value) ? $value : preg_split('/\s*,\s*/', $value); + protected function createRoleHierarchy($config, ContainerBuilder $container) + { + if (!isset($config['role_hierarchy'])) { + return; } - $container->setParameter('security.role_hierarchy.roles', $hierarchy); + $container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']); $container->remove('security.access.simple_role_voter'); $container->getDefinition('security.access.role_hierarchy_voter')->addTag('security.voter'); } protected function createAuthorization($config, ContainerBuilder $container) { - $rules = array(); - if (isset($config['access_control'])) { - $rules = $config['access_control']; - } - - if (isset($rules['rule']) && is_array($rules['rule'])) { - $rules = $rules['rule']; + if (!isset($config['access_control'])) { + return; } - foreach ($rules as $i => $access) { - $roles = isset($access['role']) ? (is_array($access['role']) ? $access['role'] : preg_split('/\s*,\s*/', $access['role'])) : array(); - $channel = null; - if (isset($access['requires_channel'])) { - $channel = $access['requires_channel']; - } - - // matcher - $path = $host = $methods = $ip = null; - if (isset($access['path'])) { - $path = $access['path']; - } - if (isset($access['host'])) { - $host = $access['host']; - } - if (count($tMethods = $this->normalizeConfig($access, 'method')) > 0) { - $methods = $tMethods; - } - if (isset($access['ip'])) { - $ip = $access['ip']; - } - - $matchAttributes = array(); - $attributes = $this->normalizeConfig($access, 'attribute'); - foreach ($attributes as $key => $attribute) { - if (isset($attribute['key'])) { - $key = $attribute['key']; - } - $matchAttributes[$key] = $attribute['pattern']; - } - $matcher = $this->createRequestMatcher($container, $path, $host, $methods, $ip, $matchAttributes); + foreach ($config['access_control'] as $access) { + $matcher = $this->createRequestMatcher( + $container, + $access['path'], + $access['host'], + count($access['methods']) === 0 ? null : $access['methods'], + $access['ip'], + $access['attributes'] + ); - $container->getDefinition('security.access_map')->addMethodCall('add', array($matcher, $roles, $channel)); + $container->getDefinition('security.access_map') + ->addMethodCall('add', array($matcher, $access['roles'], $access['requires_channel'])); } } protected function createFirewalls($config, ContainerBuilder $container) { + if (!isset($config['firewalls'])) { + return; + } + + $firewalls = $config['firewalls']; $providerIds = $this->createUserProviders($config, $container); $this->createEncoders($config, $container); - if (!$firewalls = $this->normalizeConfig($config, 'firewall')) { - return; - } - // make the ContextListener aware of the configured user providers $definition = $container->getDefinition('security.context_listener'); $arguments = $definition->getArguments(); @@ -185,16 +179,8 @@ protected function createFirewalls($config, ContainerBuilder $container) // load firewall map $mapDef = $container->getDefinition('security.firewall.map'); - $names = $map = array(); + $map = array(); foreach ($firewalls as $name => $firewall) { - if (isset($firewall['name'])) { - $name = $firewall['name']; - } - if (in_array($name, $names)) { - throw new \RuntimeException(sprintf('The firewall name must be unique. Duplicate found: "%s"', $name)); - } - $names[] = $name; - list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $providerIds, $factories); $contextId = 'security.firewall.map.context.'.$name; @@ -220,7 +206,7 @@ protected function createFirewall(ContainerBuilder $container, $id, $firewall, $ } // Security disabled? - if (isset($firewall['security']) && !$firewall['security']) { + if (false === $firewall['security']) { return array($matcher, array(), null); } @@ -228,9 +214,6 @@ protected function createFirewall(ContainerBuilder $container, $id, $firewall, $ if (isset($firewall['provider'])) { $defaultProvider = $this->getUserProviderId($firewall['provider']); } else { - if (!$providerIds) { - throw new \InvalidArgumentException('You must provide at least one authentication provider.'); - } $defaultProvider = reset($providerIds); } @@ -242,7 +225,7 @@ protected function createFirewall(ContainerBuilder $container, $id, $firewall, $ $listeners[] = new Reference('security.channel_listener'); // Context serializer listener - if (!isset($firewall['stateless']) || !$firewall['stateless']) { + if (false === $firewall['stateless']) { $contextKey = $id; if (isset($firewall['context'])) { $contextKey = $firewall['context']; @@ -252,44 +235,29 @@ protected function createFirewall(ContainerBuilder $container, $id, $firewall, $ } // Logout listener - if (array_key_exists('logout', $firewall)) { + if (isset($firewall['logout'])) { $listenerId = 'security.logout_listener.'.$id; $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener')); - + $listener->addArgument($firewall['logout']['path']); + $listener->addArgument($firewall['logout']['target']); $listeners[] = new Reference($listenerId); - if (!is_array($firewall['logout'])) { - $firewall['logout'] = array(); - } - - if (isset($firewall['logout']['path'])) { - $listener->setArgument(1, $firewall['logout']['path']); - } - - if (isset($firewall['logout']['target'])) { - $listener->setArgument(2, $firewall['logout']['target']); - } - // add session logout handler - $invalidateSession = true; - if (isset($firewall['logout']['invalidate_session'])) { - $invalidateSession = (Boolean) $firewall['logout']['invalidate_session']; - } - if (true === $invalidateSession && (!isset($firewall['stateless']) || !$firewall['stateless'])) { + if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) { $listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session'))); } // add cookie logout handler - if (count($cookies = $this->normalizeConfig($firewall['logout'], 'cookie')) > 0) { + if (count($firewall['logout']['delete_cookies']) > 0) { $cookieHandlerId = 'security.logout.handler.cookie_clearing.'.$id; $cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('security.logout.handler.cookie_clearing')); - $cookieHandler->addArgument($cookies); + $cookieHandler->addArgument($firewall['logout']['delete_cookies']); $listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId))); } // add custom handlers - foreach ($this->normalizeConfig($firewall['logout'], 'handler') as $handlerId) { + foreach ($firewall['logout']['handlers'] as $handlerId) { $listener->addMethodCall('addHandler', array(new Reference($handlerId))); } } @@ -303,7 +271,7 @@ protected function createFirewall(ContainerBuilder $container, $id, $firewall, $ $listeners[] = new Reference('security.access_listener'); // Switch user listener - if (array_key_exists('switch_user', $firewall)) { + if (isset($firewall['switch_user'])) { $listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider)); } @@ -340,13 +308,9 @@ protected function createAuthenticationListeners($container, $id, $firewall, $de foreach ($this->listenerPositions as $position) { foreach ($factories[$position] as $factory) { - $key = $factory->getKey(); - $keybis = str_replace('-', '_', $key); + $key = str_replace('-', '_', $factory->getKey()); - if (array_key_exists($keybis, $firewall)) { - $firewall[$key] = $firewall[$keybis]; - } - if (array_key_exists($key, $firewall) && $firewall[$key] !== false) { + if (isset($firewall[$key])) { $userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider; list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); @@ -359,7 +323,7 @@ protected function createAuthenticationListeners($container, $id, $firewall, $de } // Anonymous - if (array_key_exists('anonymous', $firewall)) { + if (isset($firewall['anonymous'])) { $listeners[] = new Reference('security.authentication.listener.anonymous'); $hasListeners = true; } @@ -371,66 +335,14 @@ protected function createAuthenticationListeners($container, $id, $firewall, $de return array($listeners, $providers, $defaultEntryPoint); } - // Parses user providers and returns an array of their ids - protected function createUserProviders($config, ContainerBuilder $container) - { - $providers = $this->normalizeConfig($config, 'provider'); - if (!$providers) { - return array(); - } - - $providerIds = array(); - foreach ($providers as $name => $provider) { - $id = $this->createUserDaoProvider($name, $provider, $container); - - if (in_array($id, $providerIds, true)) { - throw new \RuntimeException(sprintf('Provider names must be unique. Duplicate entry for %s.', $id)); - } - - $providerIds[] = $id; - } - - return $providerIds; - } - - protected function createListenerFactories(ContainerBuilder $container, $config) - { - // load service templates - $c = new ContainerBuilder(); - $parameterBag = $container->getParameterBag(); - $loader = new XmlFileLoader($c, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('security_factories.xml'); - - // load user-created listener factories - foreach ($this->normalizeConfig($config, 'factory', 'factories') as $factory) { - $loader->load($parameterBag->resolveValue($factory)); - } - - $tags = $c->findTaggedServiceIds('security.listener.factory'); - - $factories = array(); - foreach ($this->listenerPositions as $position) { - $factories[$position] = array(); - } - - foreach (array_keys($tags) as $tag) { - $factory = $c->get($tag); - - $factories[$factory->getPosition()][] = $factory; - } - - return $factories; - } - protected function createEncoders($config, ContainerBuilder $container) { - $encoders = $this->normalizeConfig($config, 'encoder'); - if (!$encoders) { - return array(); + if (!isset($config['encoders'])) { + return; } $encoderMap = array(); - foreach ($encoders as $class => $encoder) { + foreach ($config['encoders'] as $class => $encoder) { $encoderMap = $this->createEncoder($encoderMap, $class, $encoder, $container); } @@ -442,21 +354,6 @@ protected function createEncoders($config, ContainerBuilder $container) protected function createEncoder(array $encoderMap, $accountClass, $config, ContainerBuilder $container) { - if (is_array($config) && isset($config['class'])) { - $accountClass = $config['class']; - } - - if (empty($accountClass)) { - throw new \RuntimeException('Each encoder needs an account class.'); - } - - // a minimal message digest, or plaintext encoder - if (is_string($config)) { - $config = array( - 'algorithm' => $config, - ); - } - // a custom encoder service if (isset($config['id'])) { $container @@ -467,17 +364,12 @@ protected function createEncoder(array $encoderMap, $accountClass, $config, Cont return $encoderMap; } - // a lazy loaded, message digest or plaintext encoder - if (!isset($config['algorithm'])) { - throw new \RuntimeException('"algorithm" must be defined.'); - } - // plaintext encoder if ('plaintext' === $config['algorithm']) { $arguments = array(); if (isset($config['ignore_case'])) { - $arguments[0] = (Boolean) $config['ignore_case']; + $arguments[0] = $config['ignore_case']; } $encoderMap[$accountClass] = array( @@ -493,7 +385,7 @@ protected function createEncoder(array $encoderMap, $accountClass, $config, Cont // add optional arguments if (isset($config['encode_as_base64'])) { - $arguments[1] = (Boolean) $config['encode_as_base64']; + $arguments[1] = $config['encode_as_base64']; } else { $arguments[1] = false; } @@ -512,17 +404,23 @@ protected function createEncoder(array $encoderMap, $accountClass, $config, Cont return $encoderMap; } - // Parses a tag and returns the id for the related user provider service - protected function createUserDaoProvider($name, $provider, ContainerBuilder $container, $master = true) + // Parses user providers and returns an array of their ids + protected function createUserProviders($config, ContainerBuilder $container) { - if (isset($provider['name'])) { - $name = $provider['name']; + $providerIds = array(); + foreach ($config['providers'] as $name => $provider) { + $id = $this->createUserDaoProvider($name, $provider, $container); + $providerIds[] = $id; } - if (!$name) { - throw new \RuntimeException('You must define a name for each user provider.'); - } + return $providerIds; + } + // Parses a tag and returns the id for the related user provider service + // FIXME: Replace register() calls in this method with DefinitionDecorator + // and move the actual definition to an xml file + protected function createUserDaoProvider($name, $provider, ContainerBuilder $container, $master = true) + { $name = $this->getUserProviderId(strtolower($name)); // Existing DAO service provider @@ -533,7 +431,7 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con } // Chain provider - if (isset($provider['provider'])) { + if (count($provider['providers']) > 0) { // FIXME throw new \RuntimeException('Not implemented yet.'); } @@ -546,8 +444,9 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con ->setArguments(array( new Reference('security.user.entity_manager'), $provider['entity']['class'], - isset($provider['entity']['property']) ? $provider['entity']['property'] : null, - )); + $provider['entity']['property'], + )) + ; return $name; } @@ -560,7 +459,7 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con ->setArguments(array( new Reference('security.user.document_manager'), $provider['document']['class'], - isset($provider['document']['property']) ? $provider['document']['property'] : null, + $provider['document']['property'], )); return $name; @@ -569,27 +468,8 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con // In-memory DAO provider $definition = $container->register($name, '%security.user.provider.in_memory.class%'); $definition->setPublic(false); - foreach ($this->normalizeConfig($provider, 'user') as $username => $user) { - if (isset($user['name'])) { - $username = $user['name']; - } - - if (!array_key_exists('password', $user)) { - // if no password is provided explicitly, it means that - // the user will be used with OpenID, X.509 certificates, ... - // Let's generate a random password just to be sure this - // won't be used accidentally with other authentication schemes. - // If you want an empty password, just say so explicitly - $user['password'] = uniqid(); - } - - if (!isset($user['roles'])) { - $user['roles'] = array(); - } else { - $user['roles'] = is_array($user['roles']) ? $user['roles'] : preg_split('/\s*,\s*/', $user['roles']); - } - - $userId = $name.'_'.md5(serialize(array($username, $user['password'], $user['roles']))); + foreach ($provider['users'] as $username => $user) { + $userId = $name.'_'.md5(json_encode(array($username, $user['password'], $user['roles']))); $container ->register($userId, 'Symfony\Component\Security\Core\User\User') @@ -632,14 +512,8 @@ protected function createSwitchUserListener($container, $id, $config, $defaultPr $listener = $container->setDefinition($switchUserListenerId, new DefinitionDecorator('security.authentication.switchuser_listener')); $listener->setArgument(1, new Reference($userProvider)); $listener->setArgument(3, $id); - - if (isset($config['parameter'])) { - $listener->setArgument(5, $config['parameter']); - } - - if (isset($config['role'])) { - $listener->setArgument(6, $config['role']); - } + $listener->addArgument($config['parameter']); + $listener->addArgument($config['role']); return $switchUserListenerId; } @@ -668,42 +542,35 @@ protected function createRequestMatcher($container, $path = null, $host = null, return $this->requestMatchers[$id] = new Reference($id); } - protected function doAclLoad(array $config, ContainerBuilder $container) + protected function createListenerFactories(ContainerBuilder $container, $config) { - if (!$container->hasDefinition('security.acl')) { - $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); - $loader->load('security_acl.xml'); + if (null !== $this->factories) { + return $this->factories; } - if (isset($config['connection'])) { - $container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection'])); - } + // load service templates + $c = new ContainerBuilder(); + $parameterBag = $container->getParameterBag(); + $loader = new XmlFileLoader($c, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config')); + $loader->load('security_factories.xml'); - if (isset($config['cache'])) { - $container->setAlias('security.acl.cache', sprintf('security.acl.cache.%s', $config['cache'])); - } else { - $container->remove('security.acl.cache.doctrine'); - $container->removeAlias('security.acl.cache.doctrine.cache_impl'); + // load user-created listener factories + foreach ($config['factories'] as $factory) { + $loader->load($parameterBag->resolveValue($factory)); } - } - /** - * Returns the base path for the XSD files. - * - * @return string The XSD base path - */ - public function getXsdValidationBasePath() - { - return __DIR__.'/../Resources/config/schema'; - } + $tags = $c->findTaggedServiceIds('security.listener.factory'); - public function getNamespace() - { - return 'http://www.symfony-project.org/schema/dic/security'; - } + $factories = array(); + foreach ($this->listenerPositions as $position) { + $factories[$position] = array(); + } - public function getAlias() - { - return 'security'; + foreach (array_keys($tags) as $tag) { + $factory = $c->get($tag); + $factories[$factory->getPosition()][] = $factory; + } + + return $this->factories = $factories; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml index 3245168813088..f36a6c461a7ea 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -28,12 +28,8 @@ Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener Symfony\Component\Security\Http\Firewall\SwitchUserListener - ROLE_ALLOWED_TO_SWITCH - _switch_user Symfony\Component\Security\Http\Firewall\LogoutListener - /logout - / Symfony\Component\Security\Http\Logout\SessionLogoutHandler Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler @@ -83,14 +79,12 @@ - + - %security.logout.path% - %security.logout.target_path% @@ -162,13 +156,11 @@ - + - %security.authentication.switchuser.parameter% - %security.authentication.switchuser.role% diff --git a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php index 46436fb27d91c..b62d9b7557077 100644 --- a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php +++ b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php @@ -30,20 +30,4 @@ public function registerExtensions(ContainerBuilder $container) $container->addCompilerPass(new AddSecurityVotersPass()); $container->addCompilerPass(new AddAuthenticationProvidersPass()); } - - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php new file mode 100644 index 0000000000000..988640fb284dc --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php @@ -0,0 +1,15 @@ +load('merge_import.php', $container); + +$container->loadFromExtension('security', 'config', array( + 'firewalls' => array( + 'main' => array( + 'form_login' => false, + 'http_basic' => null, + ), + ), + 'role_hierarchy' => array( + 'FOO' => array('MOO'), + ) +)); \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php new file mode 100644 index 0000000000000..2b9be399d77c7 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php @@ -0,0 +1,15 @@ +loadFromExtension('security', 'config', array( + 'firewalls' => array( + 'main' => array( + 'form_login' => array( + 'login_path' => '/login', + ) + ) + ), + 'role_hierarchy' => array( + 'FOO' => 'BAR', + 'ADMIN' => 'USER', + ), +)); \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access.xml index c01acd61013eb..2ca9f5b9d0c7d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access.xml @@ -6,12 +6,9 @@ xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd"> - - - - - - - + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/hierarchy.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/hierarchy.xml index 795105230cd04..4c8985a79157a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/hierarchy.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/hierarchy.xml @@ -6,10 +6,8 @@ xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd"> - - ROLE_USER - ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH - ROLE_USER,ROLE_ADMIN - + ROLE_USER + ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH + ROLE_USER,ROLE_ADMIN diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml new file mode 100644 index 0000000000000..36f7b4de7208f --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml new file mode 100644 index 0000000000000..806719ab5fa07 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml new file mode 100644 index 0000000000000..a42fc99fab00c --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml @@ -0,0 +1,11 @@ +imports: + - { resource: merge_import.yml } + +security.config: + firewalls: + main: + form_login: false + http_basic: ~ + + role_hierarchy: + FOO: [MOO] \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml new file mode 100644 index 0000000000000..497fb398c770c --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml @@ -0,0 +1,9 @@ +security.config: + firewalls: + main: + form_login: + login_path: /login + + role_hierarchy: + FOO: BAR + ADMIN: USER diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index 9d6d1390ee062..c412ce0e30d00 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -28,7 +28,7 @@ public function testCreate() list($authProviderId, $listenerId, $entryPointId - ) = $factory->create($container, 'foo', array('use_forward' => true, 'failure_path' => '/foo', 'success_handler' => 'foo'), 'user_provider', 'entry_point'); + ) = $factory->create($container, 'foo', array('use_forward' => true, 'failure_path' => '/foo', 'success_handler' => 'foo', 'remember_me' => true), 'user_provider', 'entry_point'); // auth provider $this->assertEquals('auth_provider', $authProviderId); @@ -41,15 +41,8 @@ public function testCreate() $this->assertEquals(array( 'index_3' => 'foo', 'index_4' => array( - 'check_path' => '/login_check', - 'login_path' => '/login', 'use_forward' => true, - 'always_use_default_target_path' => false, - 'default_target_path' => '/', - 'target_path_parameter' => '_target_path', - 'use_referer' => false, 'failure_path' => '/foo', - 'failure_forward' => false, ), 'index_5' => new Reference('foo'), ), $definition->getArguments()); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index d76f505d93952..73df0096914ea 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -36,10 +36,10 @@ public function testUserProviders() $expectedProviders = array( 'security.authentication.provider.digest', - 'security.authentication.provider.digest_0ff1b54f2a4b7f71b2b9d6604fcca4b8', + 'security.authentication.provider.digest_23374fce51fe846516ff85bfa9add8fe', 'security.authentication.provider.basic', - 'security.authentication.provider.basic_b7f0cf21802ffc8b22cadbb255f07213', - 'security.authentication.provider.basic_98e44377704554700e68c22094b51ca4', + 'security.authentication.provider.basic_745e8583f784c83c4b4208fd281001f3', + 'security.authentication.provider.basic_af4bcce7246fb064b8e219034043d88a', 'security.authentication.provider.doctrine', 'security.authentication.provider.service', 'security.authentication.provider.anonymous', @@ -109,6 +109,16 @@ public function testAccess() } } + public function testMerge() + { + $container = $this->getContainer('merge'); + + $this->assertEquals(array( + 'FOO' => array('MOO'), + 'ADMIN' => array('USER'), + ), $container->getParameter('security.role_hierarchy.roles')); + } + protected function getContainer($file) { $container = new ContainerBuilder(); diff --git a/src/Symfony/Bundle/SwiftmailerBundle/SwiftmailerBundle.php b/src/Symfony/Bundle/SwiftmailerBundle/SwiftmailerBundle.php index 7480863e96580..8b0a3a4a10148 100644 --- a/src/Symfony/Bundle/SwiftmailerBundle/SwiftmailerBundle.php +++ b/src/Symfony/Bundle/SwiftmailerBundle/SwiftmailerBundle.php @@ -20,19 +20,4 @@ */ class SwiftmailerBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/TwigBundle/Extension/FormExtension.php b/src/Symfony/Bundle/TwigBundle/Extension/FormExtension.php index aab5c28c556ee..4f96e842111bf 100644 --- a/src/Symfony/Bundle/TwigBundle/Extension/FormExtension.php +++ b/src/Symfony/Bundle/TwigBundle/Extension/FormExtension.php @@ -123,12 +123,17 @@ public function renderRow(FieldInterface $field) * * {{ form_field(field, {}, {'separator': '+++++'}) }} * - * @param FieldInterface $field The field to render - * @param array $params Additional variables passed to the template - * @param string $resources + * @param FieldInterface $field The field to render + * @param array $attributes HTML attributes passed to the template + * @param array $parameters Additional variables passed to the template + * @param array|string $resources A resource or array of resources */ public function renderField(FieldInterface $field, array $attributes = array(), array $parameters = array(), $resources = null) { + if (null !== $resources && !is_array($resources)) { + $resources = array($resources); + } + return $this->render($field, 'field', array( 'field' => $field, 'attr' => $attributes, @@ -190,7 +195,7 @@ public function renderData(FieldInterface $field) return $field->getData(); } - protected function render(FieldInterface $field, $name, array $arguments, $resources = null) + protected function render(FieldInterface $field, $name, array $arguments, array $resources = null) { if ('field' === $name) { list($name, $template) = $this->getWidget($field, $resources); diff --git a/src/Symfony/Bundle/TwigBundle/TwigBundle.php b/src/Symfony/Bundle/TwigBundle/TwigBundle.php index dff10138a6da3..d27d85bcd2d67 100644 --- a/src/Symfony/Bundle/TwigBundle/TwigBundle.php +++ b/src/Symfony/Bundle/TwigBundle/TwigBundle.php @@ -28,20 +28,4 @@ public function registerExtensions(ContainerBuilder $container) $container->addCompilerPass(new TwigEnvironmentPass()); } - - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/TwigBundle/TwigEngine.php b/src/Symfony/Bundle/TwigBundle/TwigEngine.php index 20d7082785c3e..7fdf2d77c2a38 100644 --- a/src/Symfony/Bundle/TwigBundle/TwigEngine.php +++ b/src/Symfony/Bundle/TwigBundle/TwigEngine.php @@ -131,6 +131,6 @@ protected function load($name) return $name; } - return $this->environment->loadTemplate($this->parser->parse($name), is_array($name) ? json_encode($name) : $name); + return $this->environment->loadTemplate($this->parser->parse($name)); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php b/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php index 0a265a0004f7a..b3558f8ab3da0 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php +++ b/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php @@ -20,19 +20,4 @@ */ class WebProfilerBundle extends Bundle { - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Bundle/ZendBundle/ZendBundle.php b/src/Symfony/Bundle/ZendBundle/ZendBundle.php index e53ad48cb6476..c06f2636ab746 100644 --- a/src/Symfony/Bundle/ZendBundle/ZendBundle.php +++ b/src/Symfony/Bundle/ZendBundle/ZendBundle.php @@ -28,20 +28,4 @@ public function registerExtensions(ContainerBuilder $container) $container->addCompilerPass(new ZendLoggerWriterPass()); } - - /** - * {@inheritdoc} - */ - public function getNamespace() - { - return __NAMESPACE__; - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return __DIR__; - } } diff --git a/src/Symfony/Component/BrowserKit/Client.php b/src/Symfony/Component/BrowserKit/Client.php index 02c11dc47968f..dffb1e9327935 100644 --- a/src/Symfony/Component/BrowserKit/Client.php +++ b/src/Symfony/Component/BrowserKit/Client.php @@ -266,16 +266,39 @@ protected function getScript($request) // @codeCoverageIgnoreEnd } + /** + * Filters the request. + * + * @param Request $request The request to filter + * + * @return Request + */ protected function filterRequest(Request $request) { return $request; } + /** + * Filters the Response. + * + * @param Response $response The Response to filter + * + * @return Response + */ protected function filterResponse($response) { return $response; } + /** + * Creates a crawler. + * + * @param string $uri A uri + * @param string $content Content for the crawler to use + * @param string $type Content type + * + * @return Crawler + */ protected function createCrawlerFromContent($uri, $content, $type) { $crawler = new Crawler(null, $uri); @@ -286,6 +309,8 @@ protected function createCrawlerFromContent($uri, $content, $type) /** * Goes back in the browser history. + * + * @return Crawler */ public function back() { @@ -294,6 +319,8 @@ public function back() /** * Goes forward in the browser history. + * + * @return Crawler */ public function forward() { @@ -302,6 +329,8 @@ public function forward() /** * Reloads the current browser. + * + * @return Crawler */ public function reload() { @@ -335,6 +364,12 @@ public function restart() $this->history->clear(); } + /** + * Takes a URI and converts it to absolute if it is not already absolute. + * + * @param string $uri A uri + * @return string An absolute uri + */ protected function getAbsoluteUri($uri) { // already absolute? diff --git a/src/Symfony/Component/BrowserKit/Response.php b/src/Symfony/Component/BrowserKit/Response.php index 0ed9a4c064b66..a80129a802ae2 100644 --- a/src/Symfony/Component/BrowserKit/Response.php +++ b/src/Symfony/Component/BrowserKit/Response.php @@ -39,6 +39,11 @@ public function __construct($content = '', $status = 200, array $headers = array $this->headers = $headers; } + /** + * Converts the response object to string containing all headers and the response content. + * + * @return string The response with headers and content + */ public function __toString() { $headers = ''; diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php index 62e9f4f5dffc3..d5419a06ce769 100644 --- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php +++ b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php @@ -119,6 +119,10 @@ static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = /** * Adds brackets around each namespace if it's not already the case. + * + * @param string $source Namespace string + * + * @return string Namespaces with brackets */ static public function fixNamespaceDeclarations($source) { @@ -162,6 +166,15 @@ static public function fixNamespaceDeclarations($source) return $output; } + /** + * Writes a cache file. + * + * @param string $file Filename + * @param string $content Temporary file content + * + * @throws \RuntimeException when a cache file cannot be written + */ + static protected function writeCacheFile($file, $content) { $tmpFile = tempnam(dirname($file), basename($file)); diff --git a/src/Symfony/Component/ClassLoader/MapFileClassLoader.php b/src/Symfony/Component/ClassLoader/MapFileClassLoader.php index 8a370af645a85..0a4d5c15e64b7 100644 --- a/src/Symfony/Component/ClassLoader/MapFileClassLoader.php +++ b/src/Symfony/Component/ClassLoader/MapFileClassLoader.php @@ -12,6 +12,7 @@ namespace Symfony\Component\ClassLoader; /** + * A class loader that uses a mapping file to look up paths. * * @author Fabien Potencier */ @@ -19,6 +20,11 @@ class MapFileClassLoader { protected $map = array(); + /** + * Constructor. + * + * @param string $file Path to class mapping file + */ public function __construct($file) { $this->map = require $file; diff --git a/src/Symfony/Component/ClassLoader/UniversalClassLoader.php b/src/Symfony/Component/ClassLoader/UniversalClassLoader.php index 55d3527d57f79..0394fa496e294 100644 --- a/src/Symfony/Component/ClassLoader/UniversalClassLoader.php +++ b/src/Symfony/Component/ClassLoader/UniversalClassLoader.php @@ -102,7 +102,7 @@ public function getPrefixFallback() /** * Registers the directory to use as a fallback for namespaces. * - * @return string|array $dirs A directory path or an array of directories + * @param string|array $dirs A directory path or an array of directories */ public function registerNamespaceFallback($dirs) { @@ -112,7 +112,7 @@ public function registerNamespaceFallback($dirs) /** * Registers the directory to use as a fallback for class prefixes. * - * @return string|array $dirs A directory path or an array of directories + * @param string|array $dirs A directory path or an array of directories */ public function registerPrefixFallback($dirs) { diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 89c31bade7fa0..d7509de85b245 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -201,7 +201,7 @@ public function setHelperSet(HelperSet $helperSet) } /** - * Get the helper set associated with the command + * Get the helper set associated with the command. * * @return HelperSet The HelperSet instance associated with this command */ @@ -396,7 +396,7 @@ public function get($name) } /** - * Returns true if the command exists, false otherwise + * Returns true if the command exists, false otherwise. * * @param string $name The command name or alias * @@ -429,6 +429,8 @@ public function getNamespaces() /** * Finds a registered namespace by a name or an abbreviation. * + * @param string $namespace A namespace or abbreviation to search for + * * @return string A registered namespace * * @throws \InvalidArgumentException When namespace is incorrect or ambiguous @@ -715,11 +717,25 @@ public function renderException($e, $output) } } + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ protected function getCommandName(InputInterface $input) { return $input->getFirstArgument('command'); } + /** + * Sorts commands in alphabetical order. + * + * @param array $commands An associative array of commands to sort + * + * @return array A sorted array of commands + */ protected function sortCommands($commands) { $namespacedCommands = array(); @@ -741,6 +757,13 @@ protected function sortCommands($commands) return $namespacedCommands; } + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ protected function getAbbreviationSuggestions($abbrevs) { return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); diff --git a/src/Symfony/Component/Console/Command/HelpCommand.php b/src/Symfony/Component/Console/Command/HelpCommand.php index 49e91782dcde0..61ff032c0a383 100644 --- a/src/Symfony/Component/Console/Command/HelpCommand.php +++ b/src/Symfony/Component/Console/Command/HelpCommand.php @@ -28,7 +28,7 @@ class HelpCommand extends Command protected $command; /** - * @see Command + * {@inheritdoc} */ protected function configure() { @@ -54,13 +54,18 @@ protected function configure() ); } + /** + * Sets the command + * + * @param Command $command The command to set + */ public function setCommand(Command $command) { $this->command = $command; } /** - * @see Command + * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Symfony/Component/Console/Command/ListCommand.php b/src/Symfony/Component/Console/Command/ListCommand.php index 3d2ee7bb159f8..76fb7cbcefdba 100644 --- a/src/Symfony/Component/Console/Command/ListCommand.php +++ b/src/Symfony/Component/Console/Command/ListCommand.php @@ -26,7 +26,7 @@ class ListCommand extends Command { /** - * @see Command + * {@inheritdoc} */ protected function configure() { @@ -54,7 +54,7 @@ protected function configure() } /** - * @see Command + * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Symfony/Component/Console/Helper/FormatterHelper.php b/src/Symfony/Component/Console/Helper/FormatterHelper.php index 7da190631fad9..5a05aa049a994 100644 --- a/src/Symfony/Component/Console/Helper/FormatterHelper.php +++ b/src/Symfony/Component/Console/Helper/FormatterHelper.php @@ -67,6 +67,13 @@ public function formatBlock($messages, $style, $large = false) return implode("\n", $messages); } + /** + * Returns the length of a string, uses mb_strlen if it is available. + * + * @param string $string The string to check its length + * + * @return integer The length of the string + */ protected function strlen($string) { return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); @@ -74,6 +81,8 @@ protected function strlen($string) /** * Returns the helper's canonical name + * + * @return string The canonical name of the helper */ public function getName() { diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index 5662e5f69bdcb..6ad48121442aa 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -252,4 +252,36 @@ public function hasParameterOption($values) return false; } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before it has been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + if (!is_array($values)) { + $values = array($values); + } + + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + foreach ($values as $value) { + if (0 === strpos($token, $value)) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } else { + return array_shift($this->tokens); + } + } + } + } + + return $default; + } } diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php index e9572d71ed297..56c5fad9ab518 100644 --- a/src/Symfony/Component/Console/Input/ArrayInput.php +++ b/src/Symfony/Component/Console/Input/ArrayInput.php @@ -82,6 +82,34 @@ public function hasParameterOption($values) return false; } + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before it has been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + if (!is_array($values)) { + $values = array($values); + } + + foreach ($this->parameters as $k => $v) { + if (is_int($k) && in_array($v, $values)) { + return true; + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + /** * Processes command line arguments. */ diff --git a/src/Symfony/Component/Console/Input/Input.php b/src/Symfony/Component/Console/Input/Input.php index 8fcf9899251f6..aba45273a7bc9 100644 --- a/src/Symfony/Component/Console/Input/Input.php +++ b/src/Symfony/Component/Console/Input/Input.php @@ -64,6 +64,8 @@ public function bind(InputDefinition $definition) abstract protected function parse(); /** + * Validates the input. + * * @throws \RuntimeException When not enough arguments are given */ public function validate() @@ -73,11 +75,21 @@ public function validate() } } + /** + * Checks if the input is interactive. + * + * @return Boolean Returns true if the input is interactive + */ public function isInteractive() { return $this->interactive; } + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ public function setInteractive($interactive) { $this->interactive = (Boolean) $interactive; diff --git a/src/Symfony/Component/Console/Input/InputDefinition.php b/src/Symfony/Component/Console/Input/InputDefinition.php index a4ed4ae3a2a9b..9a7baff2a60e5 100644 --- a/src/Symfony/Component/Console/Input/InputDefinition.php +++ b/src/Symfony/Component/Console/Input/InputDefinition.php @@ -42,6 +42,11 @@ public function __construct(array $definition = array()) $this->setDefinition($definition); } + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + */ public function setDefinition(array $definition) { $arguments = array(); @@ -297,6 +302,8 @@ public function hasShortcut($name) /** * Gets an InputOption by shortcut. * + * @param string $shortcut the Shortcut name + * * @return InputOption An InputOption object */ public function getOptionForShortcut($shortcut) diff --git a/src/Symfony/Component/Console/Input/InputInterface.php b/src/Symfony/Component/Console/Input/InputInterface.php index bf943716a4e1a..53147732987ab 100644 --- a/src/Symfony/Component/Console/Input/InputInterface.php +++ b/src/Symfony/Component/Console/Input/InputInterface.php @@ -31,11 +31,24 @@ function getFirstArgument(); * This method is to be used to introspect the input parameters * before it has been validated. It must be used carefully. * - * @param string $value The value to look for in the raw parameters + * @param string|array $value The values to look for in the raw parameters (can be an array) * * @return Boolean true if the value is contained in the raw parameters */ - function hasParameterOption($value); + function hasParameterOption($values); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before it has been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + function getParameterOption($values, $default = false); /** * Binds the current Input instance with the given arguments and options. @@ -63,6 +76,7 @@ function getArguments(); /** * Get argument by name. * + * @param string $name The name of the argument * @return mixed */ function getArgument($name); @@ -72,6 +86,12 @@ function getArgument($name); */ function getOptions(); + /** + * Get an option by name. + * + * @param string $name The name of the option + * @return mixed + */ function getOption($name); /** diff --git a/src/Symfony/Component/Console/Input/StringInput.php b/src/Symfony/Component/Console/Input/StringInput.php index 64d9772af2d99..7a7575996b9c8 100644 --- a/src/Symfony/Component/Console/Input/StringInput.php +++ b/src/Symfony/Component/Console/Input/StringInput.php @@ -39,6 +39,9 @@ public function __construct($input, InputDefinition $definition = null) } /** + * Tokenizes a string. + * + * @param string $input The input to tokenise * @throws \InvalidArgumentException When unable to parse input (should never happen) */ protected function tokenize($input) diff --git a/src/Symfony/Component/Console/Output/Output.php b/src/Symfony/Component/Console/Output/Output.php index 388848614af0c..acc1a2f03c9b2 100644 --- a/src/Symfony/Component/Console/Output/Output.php +++ b/src/Symfony/Component/Console/Output/Output.php @@ -179,6 +179,12 @@ protected function format($message) } /** + * Replaces the starting style of the output. + * + * @param array $match + * + * @return string The replaced style + * * @throws \InvalidArgumentException When style is unknown */ protected function replaceStartStyle($match) @@ -220,6 +226,13 @@ protected function replaceStartStyle($match) return "\033[".implode(';', $codes).'m'; } + /** + * Replaces the end style. + * + * @param string $match The text to match + * + * @return string The end style + */ protected function replaceEndStyle($match) { if (!$this->decorated) { diff --git a/src/Symfony/Component/CssSelector/Node/AttribNode.php b/src/Symfony/Component/CssSelector/Node/AttribNode.php index 30128826cdb28..3471986d2d166 100644 --- a/src/Symfony/Component/CssSelector/Node/AttribNode.php +++ b/src/Symfony/Component/CssSelector/Node/AttribNode.php @@ -30,6 +30,15 @@ class AttribNode implements NodeInterface protected $operator; protected $value; + /** + * Constructor. + * + * @param NodeInterface $selector The XPath selector + * @param string $namespace The namespace + * @param string $attrib The attribute + * @param string $operator The operator + * @param string $value The value + */ public function __construct($selector, $namespace, $attrib, $operator, $value) { $this->selector = $selector; @@ -39,6 +48,9 @@ public function __construct($selector, $namespace, $attrib, $operator, $value) $this->value = $value; } + /** + * {@inheritDoc} + */ public function __toString() { if ($this->operator == 'exists') { @@ -49,7 +61,7 @@ public function __toString() } /** - * @throws SyntaxError When unknown operator is found + * {@inheritDoc} */ public function toXpath() { @@ -88,6 +100,11 @@ public function toXpath() return $path; } + /** + * Returns the XPath Attribute + * + * @return string The XPath attribute + */ protected function xpathAttrib() { // FIXME: if attrib is *? @@ -98,6 +115,11 @@ protected function xpathAttrib() return sprintf('@%s:%s', $this->namespace, $this->attrib); } + /** + * Returns a formatted attribute + * + * @return string The formatted attributep + */ protected function formatAttrib() { if ($this->namespace == '*') { diff --git a/src/Symfony/Component/CssSelector/Node/ClassNode.php b/src/Symfony/Component/CssSelector/Node/ClassNode.php index de7e931c4fe57..850b44ec892ea 100644 --- a/src/Symfony/Component/CssSelector/Node/ClassNode.php +++ b/src/Symfony/Component/CssSelector/Node/ClassNode.php @@ -26,17 +26,29 @@ class ClassNode implements NodeInterface protected $selector; protected $className; + /** + * The constructor. + * + * @param NodeInterface $selector The XPath Selector + * @param string $className The class name + */ public function __construct($selector, $className) { $this->selector = $selector; $this->className = $className; } + /** + * {@inheritDoc} + */ public function __toString() { return sprintf('%s[%s.%s]', __CLASS__, $this->selector, $this->className); } + /** + * {@inheritDoc} + */ public function toXpath() { $selXpath = $this->selector->toXpath(); diff --git a/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php b/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php index 1662d59f58433..80e88e130a51f 100644 --- a/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php +++ b/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php @@ -34,6 +34,13 @@ class CombinedSelectorNode implements NodeInterface protected $combinator; protected $subselector; + /** + * The constructor. + * + * @param NodeInterface $selector The XPath selector + * @param string $combinator The combinator + * @param NodeInterface $subselector The sub XPath selector + */ public function __construct($selector, $combinator, $subselector) { $this->selector = $selector; @@ -41,6 +48,9 @@ public function __construct($selector, $combinator, $subselector) $this->subselector = $subselector; } + /** + * {@inheritDoc} + */ public function __toString() { $comb = $this->combinator == ' ' ? '' : $this->combinator; @@ -49,6 +59,7 @@ public function __toString() } /** + * {@inheritDoc} * @throws SyntaxError When unknown combinator is found */ public function toXpath() @@ -63,6 +74,12 @@ public function toXpath() return $this->$method($path, $this->subselector); } + /** + * Joins a NodeInterface into the XPath of this object. + * + * @param XPathExpr $xpath The XPath expression for this object + * @param NodeInterface $sub The NodeInterface object to add + */ protected function _xpath_descendant($xpath, $sub) { // when sub is a descendant in any way of xpath @@ -71,6 +88,12 @@ protected function _xpath_descendant($xpath, $sub) return $xpath; } + /** + * Joins a NodeInterface as a child of this object. + * + * @param XPathExpr $xpath The parent XPath expression + * @param NodeInterface $sub The NodeInterface object to add + */ protected function _xpath_child($xpath, $sub) { // when sub is an immediate child of xpath @@ -79,6 +102,12 @@ protected function _xpath_child($xpath, $sub) return $xpath; } + /** + * Joins an XPath expression as an adjacent of another. + * + * @param XPathExpr $xpath The parent XPath expression + * @param NodeInterface $sub The adjacent XPath expression + */ protected function _xpath_direct_adjacent($xpath, $sub) { // when sub immediately follows xpath @@ -89,6 +118,12 @@ protected function _xpath_direct_adjacent($xpath, $sub) return $xpath; } + /** + * Joins an XPath expression as an indirect adjacent of another. + * + * @param XPathExpr $xpath The parent XPath expression + * @param NodeInterface $sub The indirect adjacent NodeInterface object + */ protected function _xpath_indirect_adjacent($xpath, $sub) { // when sub comes somewhere after xpath as a sibling diff --git a/src/Symfony/Component/CssSelector/Node/ElementNode.php b/src/Symfony/Component/CssSelector/Node/ElementNode.php index 86ffd57721091..683d36562496b 100644 --- a/src/Symfony/Component/CssSelector/Node/ElementNode.php +++ b/src/Symfony/Component/CssSelector/Node/ElementNode.php @@ -26,17 +26,31 @@ class ElementNode implements NodeInterface protected $namespace; protected $element; + /** + * Constructor. + * + * @param string $namespace Namespace + * @param string $element Element + */ public function __construct($namespace, $element) { $this->namespace = $namespace; $this->element = $element; } + /** + * {@inheritDoc} + */ public function __toString() { return sprintf('%s[%s]', __CLASS__, $this->formatElement()); } + /** + * Formats the element into a string. + * + * @return string Element as an XPath string + */ public function formatElement() { if ($this->namespace == '*') { @@ -46,6 +60,9 @@ public function formatElement() return sprintf('%s|%s', $this->namespace, $this->element); } + /** + * {@inheritDoc} + */ public function toXpath() { if ($this->namespace == '*') { diff --git a/src/Symfony/Component/CssSelector/Node/FunctionNode.php b/src/Symfony/Component/CssSelector/Node/FunctionNode.php index 9f7467b37d23c..8de049672d80e 100644 --- a/src/Symfony/Component/CssSelector/Node/FunctionNode.php +++ b/src/Symfony/Component/CssSelector/Node/FunctionNode.php @@ -31,6 +31,14 @@ class FunctionNode implements NodeInterface protected $name; protected $expr; + /** + * Constructor. + * + * @param NodeInterface $selector The XPath expression + * @param string $type + * @param string $name + * @param XPathExpr $expr + */ public function __construct($selector, $type, $name, $expr) { $this->selector = $selector; @@ -39,12 +47,16 @@ public function __construct($selector, $type, $name, $expr) $this->expr = $expr; } + /** + * {@inheritDoc} + */ public function __toString() { return sprintf('%s[%s%s%s(%s)]', __CLASS__, $this->selector, $this->type, $this->name, $this->expr); } /** + * {@inheritDoc} * @throws SyntaxError When unsupported or unknown pseudo-class is found */ public function toXpath() @@ -61,6 +73,15 @@ public function toXpath() return $this->$method($sel_path, $this->expr); } + /** + * undocumented function + * + * @param XPathExpr $xpath + * @param mixed $expr + * @param string $last + * @param string $addNameTest + * @return XPathExpr + */ protected function _xpath_nth_child($xpath, $expr, $last = false, $addNameTest = true) { list($a, $b) = $this->parseSeries($expr); @@ -124,11 +145,25 @@ protected function _xpath_nth_child($xpath, $expr, $last = false, $addNameTest = -1n+6 means elements 6 and previous */ } + /** + * undocumented function + * + * @param XPathExpr $xpath + * @param XPathExpr $expr + * @return XPathExpr + */ protected function _xpath_nth_last_child($xpath, $expr) { return $this->_xpath_nth_child($xpath, $expr, true); } + /** + * undocumented function + * + * @param XPathExpr $xpath + * @param XPathExpr $expr + * @return XPathExpr + */ protected function _xpath_nth_of_type($xpath, $expr) { if ($xpath->getElement() == '*') { @@ -138,11 +173,25 @@ protected function _xpath_nth_of_type($xpath, $expr) return $this->_xpath_nth_child($xpath, $expr, false, false); } + /** + * undocumented function + * + * @param XPathExpr $xpath + * @param XPathExpr $expr + * @return XPathExpr + */ protected function _xpath_nth_last_of_type($xpath, $expr) { return $this->_xpath_nth_child($xpath, $expr, true, false); } + /** + * undocumented function + * + * @param XPathExpr $xpath + * @param XPathExpr $expr + * @return XPathExpr + */ protected function _xpath_contains($xpath, $expr) { // text content, minus tags, must contain expr @@ -159,6 +208,13 @@ protected function _xpath_contains($xpath, $expr) return $xpath; } + /** + * undocumented function + * + * @param XPathExpr $xpath + * @param XPathExpr $expr + * @return XPathExpr + */ protected function _xpath_not($xpath, $expr) { // everything for which not expr applies @@ -170,7 +226,12 @@ protected function _xpath_not($xpath, $expr) return $xpath; } - // Parses things like '1n+2', or 'an+b' generally, returning (a, b) + /** + * Parses things like '1n+2', or 'an+b' generally, returning (a, b) + * + * @param mixed $s + * @return array + */ protected function parseSeries($s) { if ($s instanceof ElementNode) { diff --git a/src/Symfony/Component/CssSelector/Node/HashNode.php b/src/Symfony/Component/CssSelector/Node/HashNode.php index 0e72ce0d6ccf6..8c557b547cc58 100644 --- a/src/Symfony/Component/CssSelector/Node/HashNode.php +++ b/src/Symfony/Component/CssSelector/Node/HashNode.php @@ -26,17 +26,29 @@ class HashNode implements NodeInterface protected $selector; protected $id; + /** + * Constructor. + * + * @param NodeInterface $selector The NodeInterface object + * @param string $id The ID + */ public function __construct($selector, $id) { $this->selector = $selector; $this->id = $id; } + /** + * {@inheritDoc} + */ public function __toString() { return sprintf('%s[%s#%s]', __CLASS__, $this->selector, $this->id); } + /** + * {@inheritDoc} + */ public function toXpath() { $path = $this->selector->toXpath(); diff --git a/src/Symfony/Component/CssSelector/Node/NodeInterface.php b/src/Symfony/Component/CssSelector/Node/NodeInterface.php index f1e8392d045f5..2249617688b12 100644 --- a/src/Symfony/Component/CssSelector/Node/NodeInterface.php +++ b/src/Symfony/Component/CssSelector/Node/NodeInterface.php @@ -21,7 +21,17 @@ */ interface NodeInterface { + /** + * Returns a string representation of the object. + * + * @return string The string representation + */ function __toString(); + /** + * @return XPathExpr The XPath expression + * + * @throws SyntaxError When unknown operator is found + */ function toXpath(); } diff --git a/src/Symfony/Component/CssSelector/Node/OrNode.php b/src/Symfony/Component/CssSelector/Node/OrNode.php index c0ea77569502a..af13a6caff50c 100644 --- a/src/Symfony/Component/CssSelector/Node/OrNode.php +++ b/src/Symfony/Component/CssSelector/Node/OrNode.php @@ -25,16 +25,27 @@ class OrNode implements NodeInterface { protected $items; + /** + * Constructor. + * + * @param array $items An array of NodeInterface objects + */ public function __construct($items) { $this->items = $items; } + /** + * {@inheritDoc} + */ public function __toString() { return sprintf('%s(%s)', __CLASS__, $this->items); } + /** + * {@inheritDoc} + */ public function toXpath() { $paths = array(); diff --git a/src/Symfony/Component/CssSelector/Node/PseudoNode.php b/src/Symfony/Component/CssSelector/Node/PseudoNode.php index d55009d68d34c..d0d9166ca4283 100644 --- a/src/Symfony/Component/CssSelector/Node/PseudoNode.php +++ b/src/Symfony/Component/CssSelector/Node/PseudoNode.php @@ -34,6 +34,11 @@ class PseudoNode implements NodeInterface protected $ident; /** + * Constructor. + * + * @param NodeInterface $element The NodeInterface element + * @param string $type Node type + * @param string $ident The ident * @throws SyntaxError When incorrect PseudoNode type is given */ public function __construct($element, $type, $ident) @@ -48,12 +53,16 @@ public function __construct($element, $type, $ident) $this->ident = $ident; } + /** + * {@inheritDoc} + */ public function __toString() { return sprintf('%s[%s%s%s]', __CLASS__, $this->element, $this->type, $this->ident); } /** + * {@inheritDoc} * @throws SyntaxError When unsupported or unknown pseudo-class is found */ public function toXpath() @@ -71,6 +80,11 @@ public function toXpath() return $this->$method($el_xpath); } + /** + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified XPath expression + */ protected function xpath_checked($xpath) { // FIXME: is this really all the elements? @@ -80,6 +94,8 @@ protected function xpath_checked($xpath) } /** + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified XPath expression * @throws SyntaxError If this element is the root element */ protected function xpath_root($xpath) @@ -88,6 +104,12 @@ protected function xpath_root($xpath) throw new SyntaxError(); } + /** + * Marks this XPath expression as the first child. + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression + */ protected function xpath_first_child($xpath) { $xpath->addStarPrefix(); @@ -97,6 +119,12 @@ protected function xpath_first_child($xpath) return $xpath; } + /** + * Sets the XPath to be the last child. + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression + */ protected function xpath_last_child($xpath) { $xpath->addStarPrefix(); @@ -106,6 +134,12 @@ protected function xpath_last_child($xpath) return $xpath; } + /** + * Sets the XPath expression to be the first of type. + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression + */ protected function xpath_first_of_type($xpath) { if ($xpath->getElement() == '*') { @@ -118,6 +152,10 @@ protected function xpath_first_of_type($xpath) } /** + * Sets the XPath expression to be the last of type. + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression * @throws SyntaxError Because *:last-of-type is not implemented */ protected function xpath_last_of_type($xpath) @@ -131,6 +169,12 @@ protected function xpath_last_of_type($xpath) return $xpath; } + /** + * Sets the XPath expression to be the only child. + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression + */ protected function xpath_only_child($xpath) { $xpath->addNameTest(); @@ -141,6 +185,10 @@ protected function xpath_only_child($xpath) } /** + * Sets the XPath expression to be only of type. + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression * @throws SyntaxError Because *:only-of-type is not implemented */ protected function xpath_only_of_type($xpath) @@ -153,6 +201,12 @@ protected function xpath_only_of_type($xpath) return $xpath; } + /** + * undocumented function + * + * @param XPathExpr $xpath The XPath expression + * @return XPathExpr The modified expression + */ protected function xpath_empty($xpath) { $xpath->addCondition('not(*) and not(normalize-space())'); diff --git a/src/Symfony/Component/CssSelector/Parser.php b/src/Symfony/Component/CssSelector/Parser.php index 6861f80899b6a..612e556d2607b 100644 --- a/src/Symfony/Component/CssSelector/Parser.php +++ b/src/Symfony/Component/CssSelector/Parser.php @@ -25,7 +25,16 @@ class Parser { /** + * Translates a CSS expression to its XPath equivalent. + * Optionally, a prefix can be added to the resulting XPath + * expression with the $prefix parameter. + * * @throws SyntaxError When got None for xpath expression + * + * @param mixed $cssExpr The CSS expression. + * @param string $prefix An optional prefix for the XPath expression. + * + * @return string */ static public function cssToXpath($cssExpr, $prefix = 'descendant-or-self::') { @@ -62,7 +71,14 @@ static public function cssToXpath($cssExpr, $prefix = 'descendant-or-self::') } /** + * Parses an expression and returns the Node object that represents + * the parsed expression. + * * @throws \Exception When tokenizer throws it while parsing + * + * @param string $string The expression to parse + * + * @return Node\NodeInterface */ public function parse($string) { @@ -79,6 +95,14 @@ public function parse($string) } } + /** + * Parses a selector group contained in $stream and returns + * the Node object that represents the expression. + * + * @param TokenStream $stream The stream to parse. + * + * @return Node\NodeInterface + */ protected function parseSelectorGroup($stream) { $result = array(); @@ -99,7 +123,14 @@ protected function parseSelectorGroup($stream) } /** + * Parses a selector contained in $stream and returns the Node + * object that represents it. + * * @throws SyntaxError When expected selector but got something else + * + * @param TokenStrem $stream The stream containing the selector. + * + * @return Node\NodeInterface */ protected function parseSelector($stream) { @@ -128,7 +159,14 @@ protected function parseSelector($stream) } /** + * Parses a simple selector (the current token) from $stream and returns + * the resulting Node object. + * * @throws SyntaxError When expected symbol but got something else + * + * @param TokenStream The stream containing the selector. + * + * @return Node\NodeInterface */ protected function parseSimpleSelector($stream) { @@ -228,7 +266,16 @@ protected function parseSimpleSelector($stream) } /** + * Parses an attribute from a selector contained in $stream and returns + * the resulting AttribNode object. + * * @throws SyntaxError When encountered unexpected selector + * + * @param Node\NodeInterface $selector The selector object whose attribute + * is to be parsed. + * @param TokenStream $strem The container token stream. + * + * @return Node\AttribNode */ protected function parseAttrib($selector, $stream) { diff --git a/src/Symfony/Component/CssSelector/Token.php b/src/Symfony/Component/CssSelector/Token.php index 0dcb750167045..7f1fdfb4f291d 100644 --- a/src/Symfony/Component/CssSelector/Token.php +++ b/src/Symfony/Component/CssSelector/Token.php @@ -25,6 +25,13 @@ class Token protected $value; protected $position; + /** + * Constructor. + * + * @param string $type The type of this token. + * @param mixed $value The value of this token. + * @param int $position The order of this token. + */ public function __construct($type, $value, $position) { $this->type = $type; @@ -32,16 +39,33 @@ public function __construct($type, $value, $position) $this->position = $position; } + /** + * Get a string representation of this token. + * + * @return string + */ public function __toString() { return (string) $this->value; } + /** + * Answers whether this token's type equals to $type. + * + * @param string $type The type to test against this token's one. + * + * @return bool + */ public function isType($type) { return $this->type == $type; } + /** + * Get the position of this token. + * + * @return int + */ public function getPosition() { return $this->position; diff --git a/src/Symfony/Component/CssSelector/TokenStream.php b/src/Symfony/Component/CssSelector/TokenStream.php index 5e262530aedcd..bbfbd88213a8f 100644 --- a/src/Symfony/Component/CssSelector/TokenStream.php +++ b/src/Symfony/Component/CssSelector/TokenStream.php @@ -27,6 +27,12 @@ class TokenStream protected $peeked; protected $peeking; + /** + * Constructor. + * + * @param array $tokens The tokens that make the stream. + * @param mixed $source The source of the stream. + */ public function __construct($tokens, $source = null) { $this->used = array(); @@ -36,11 +42,23 @@ public function __construct($tokens, $source = null) $this->peeking = false; } + /** + * Get the tokens that have already been visited in this stream. + * + * @return array + */ public function getUsed() { return $this->used; } + /** + * Get the next token in the stream or null if there is none. + * Note that if this stream was set to be peeking its behavior + * will be restored to not peeking after this operation. + * + * @return mixed + */ public function next() { if ($this->peeking) { @@ -60,6 +78,16 @@ public function next() return $next; } + /** + * Peeks for the next token in this stream. This means that the next token + * will be returned but it won't be considered as used (visited) until the + * next() method is invoked. + * If there are no remaining tokens null will be returned. + * + * @see next() + * + * @return mixed + */ public function peek() { if (!$this->peeking) { diff --git a/src/Symfony/Component/CssSelector/Tokenizer.php b/src/Symfony/Component/CssSelector/Tokenizer.php index 339eed25e4821..d6f934f5f5f96 100644 --- a/src/Symfony/Component/CssSelector/Tokenizer.php +++ b/src/Symfony/Component/CssSelector/Tokenizer.php @@ -21,6 +21,14 @@ */ class Tokenizer { + /** + * Takes a CSS selector and returns an array holding the Tokens + * it contains. + * + * @param string $s The selector to lex. + * + * @return array Token[] + */ public function tokenize($s) { if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { @@ -95,7 +103,16 @@ public function tokenize($s) } /** + * Tokenizes a quoted string (i.e. 'A string quoted with \' characters'), + * and returns an array holding the unquoted string contained by $s and + * the new position from which tokenizing should take over. + * * @throws SyntaxError When expected closing is not found + * + * @param string $s The selector string containing the quoted string. + * @param int $pos The starting position for the quoted string. + * + * @return array */ protected function tokenizeEscapedString($s, $pos) { @@ -125,7 +142,13 @@ protected function tokenizeEscapedString($s, $pos) } /** + * Unescapes a string literal and returns the unescaped string. + * * @throws SyntaxError When invalid escape sequence is found + * + * @param string $literal The string literal to unescape. + * + * @return string */ protected function unescapeStringLiteral($literal) { @@ -143,7 +166,16 @@ protected function unescapeStringLiteral($literal) } /** + * Lexes selector $s and returns an array holding the name of the symbol + * contained in it and the new position from which tokenizing should take + * over. + * * @throws SyntaxError When Unexpected symbol is found + * + * @param string $s The selector string. + * @param int $pos The position in $s at which the symbol starts. + * + * @return array */ protected function tokenizeSymbol($s, $pos) { diff --git a/src/Symfony/Component/CssSelector/XPathExpr.php b/src/Symfony/Component/CssSelector/XPathExpr.php index 52262e6efcf7c..d5a54caf06307 100644 --- a/src/Symfony/Component/CssSelector/XPathExpr.php +++ b/src/Symfony/Component/CssSelector/XPathExpr.php @@ -27,6 +27,15 @@ class XPathExpr protected $condition; protected $starPrefix; + /** + * Constructor. + * + * @param string $prefix Prefix for the XPath expression. + * @param string $path Actual path of the expression. + * @param string $element The element in the expression. + * @param string $condition A condition for the expression. + * @param bool $starPrefix Indicates whether to use a star prefix. + */ public function __construct($prefix = null, $path = null, $element = '*', $condition = null, $starPrefix = false) { $this->prefix = $prefix; @@ -36,31 +45,61 @@ public function __construct($prefix = null, $path = null, $element = '*', $condi $this->starPrefix = $starPrefix; } + /** + * Get the prefix of this XPath expression. + * + * @return string + */ public function getPrefix() { return $this->prefix; } + /** + * Get the path of this XPath expression. + * + * @return string + */ public function getPath() { return $this->path; } + /** + * Answers whether this XPath expression has a star prefix. + * + * @return bool + */ public function hasStarPrefix() { return $this->starPrefix; } + /** + * Get the element of this XPath expression. + * + * @return string + */ public function getElement() { return $this->element; } + /** + * Get the condition of this XPath expression. + * + * @return string + */ public function getCondition() { return $this->condition; } + /** + * Get a string representation for this XPath expression. + * + * @return string + */ public function __toString() { $path = ''; @@ -81,6 +120,12 @@ public function __toString() return $path; } + /** + * Adds a condition to this XPath expression. + * Any pre-existant condition will be ANDed to it. + * + * @param string $condition The condition to add. + */ public function addCondition($condition) { if ($this->condition) { @@ -90,6 +135,12 @@ public function addCondition($condition) } } + /** + * Adds a prefix to this XPath expression. + * It will be prepended to any pre-existant prefixes. + * + * @param string $prefix The prefix to add. + */ public function addPrefix($prefix) { if ($this->prefix) { @@ -99,6 +150,11 @@ public function addPrefix($prefix) } } + /** + * Adds a condition to this XPath expression using the name of the element + * as the desired value. + * This method resets the element to '*'. + */ public function addNameTest() { if ($this->element == '*') { @@ -110,6 +166,11 @@ public function addNameTest() $this->element = '*'; } + /** + * Adds a star prefix to this XPath expression. + * This method will prepend a '*' to the path and set the star prefix flag + * to true. + */ public function addStarPrefix() { /* @@ -125,6 +186,14 @@ public function addStarPrefix() $this->starPrefix = true; } + /** + * Joins this XPath expression with $other (another XPath expression) using + * $combiner to join them. + * + * @param string $combiner The combiner string. + * @param XPathExpr $other The other XPath expression to combine with + * this one. + */ public function join($combiner, $other) { $prefix = (string) $this; @@ -143,6 +212,13 @@ public function join($combiner, $other) $this->condition = $other->GetCondition(); } + /** + * Get an XPath literal for $s. + * + * @param mixed $s Can either be a Node\ElementNode or a string. + * + * @return string + */ static public function xpathLiteral($s) { if ($s instanceof Node\ElementNode) { diff --git a/src/Symfony/Component/CssSelector/XPathExprOr.php b/src/Symfony/Component/CssSelector/XPathExprOr.php index c5347ba1306e6..21aa5a95494a8 100644 --- a/src/Symfony/Component/CssSelector/XPathExprOr.php +++ b/src/Symfony/Component/CssSelector/XPathExprOr.php @@ -23,12 +23,23 @@ */ class XPathExprOr extends XPathExpr { + /** + * Constructor. + * + * @param array $items The items in the expression. + * @param string $prefix Optional prefix for the expression. + */ public function __construct($items, $prefix = null) { $this->items = $items; $this->prefix = $prefix; } + /** + * Get a string representation of this |'d expression. + * + * @return string + */ public function __toString() { $prefix = $this->prefix; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php index 4b93f9ac0f3c3..dc2654e77294c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php @@ -41,7 +41,7 @@ public function process(ContainerBuilder $container) // non-synthetic, non-abstract service has class if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) { - if ($definition->getFactoryService()) { + if ($definition->getFactoryClass() || $definition->getFactoryService()) { throw new \RuntimeException(sprintf( 'Please add the class to service "%s" even if it is constructed ' .'by a factory since we might need to add method calls based on ' diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index a0cc8213f541f..d3961ca57432b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -49,8 +49,9 @@ protected function resolveDefinition($id, DefinitionDecorator $definition) $def->setClass($parentDef->getClass()); $def->setArguments($parentDef->getArguments()); $def->setMethodCalls($parentDef->getMethodCalls()); - $def->setFactoryService($parentDef->getFactoryService()); + $def->setFactoryClass($parentDef->getFactoryClass()); $def->setFactoryMethod($parentDef->getFactoryMethod()); + $def->setFactoryService($parentDef->getFactoryService()); $def->setConfigurator($parentDef->getConfigurator()); $def->setFile($parentDef->getFile()); $def->setPublic($parentDef->isPublic()); @@ -60,6 +61,9 @@ protected function resolveDefinition($id, DefinitionDecorator $definition) if (isset($changes['class'])) { $def->setClass($definition->getClass()); } + if (isset($changes['factory_class'])) { + $def->setFactoryClass($definition->getFactoryClass()); + } if (isset($changes['factory_method'])) { $def->setFactoryMethod($definition->getFactoryMethod()); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInterfaceInjectorsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInterfaceInjectorsPass.php index 1373665cd0391..2fecee1b6694e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInterfaceInjectorsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInterfaceInjectorsPass.php @@ -32,7 +32,7 @@ public function process(ContainerBuilder $container) $loaded = false; foreach ($container->getInterfaceInjectors() as $injector) { - if (null !== $definition->getFactoryService()) { + if (null !== $definition->getFactoryClass() || null !== $definition->getFactoryService()) { continue; } diff --git a/src/Symfony/Component/DependencyInjection/Configuration/ArrayNode.php b/src/Symfony/Component/DependencyInjection/Configuration/ArrayNode.php index 9a9fc165f8716..e5b75a5a0a96a 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/ArrayNode.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/ArrayNode.php @@ -2,23 +2,117 @@ namespace Symfony\Component\DependencyInjection\Configuration; -use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Configuration\Exception\InvalidConfigurationException; +use Symfony\Component\DependencyInjection\Configuration\Exception\DuplicateKeyException; use Symfony\Component\DependencyInjection\Configuration\Exception\InvalidTypeException; +use Symfony\Component\DependencyInjection\Configuration\Exception\UnsetKeyException; +use Symfony\Component\DependencyInjection\Extension\Extension; +/** + * Represents an ARRAY node in the config tree. + * + * @author Johannes M. Schmitt + */ class ArrayNode extends BaseNode implements PrototypeNodeInterface { - protected $normalizeTransformations; + protected $xmlRemappings; protected $children; protected $prototype; protected $keyAttribute; + protected $allowFalse; + protected $allowNewKeys; + protected $addIfNotSet; + protected $minNumberOfElements; + protected $performDeepMerging; - public function __construct($name, NodeInterface $parent = null, array $beforeTransformations = array(), array $afterTransformations = array(), array $normalizeTransformations = array(), $keyAttribute = null) + public function __construct($name, NodeInterface $parent = null) { - parent::__construct($name, $parent, $beforeTransformations, $afterTransformations); + parent::__construct($name, $parent); $this->children = array(); - $this->normalizeTransformations = $normalizeTransformations; - $this->keyAttribute = $keyAttribute; + $this->xmlRemappings = array(); + $this->allowFalse = false; + $this->addIfNotSet = false; + $this->allowNewKeys = true; + $this->performDeepMerging = true; + $this->minNumberOfElements = 0; + } + + /** + * Sets the xml remappings that should be performed. + * + * @param array $remappings an array of the form array(array(string, string)) + * @return void + */ + public function setXmlRemappings(array $remappings) + { + $this->xmlRemappings = $remappings; + } + + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + * + * @param integer $number + * @return void + */ + public function setMinNumberOfElements($number) + { + $this->minNumberOfElements = $number; + } + + /** + * The name of the attribute that should be used as key. + * + * This is only relevant for XML configurations, and only in combination + * with a prototype based node. + * + * @param string $attribute + * @return void + */ + public function setKeyAttribute($attribute) + { + $this->keyAttribute = $attribute; + } + + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + * + * @param Boolean $boolean + * @return void + */ + public function setAddIfNotSet($boolean) + { + $this->addIfNotSet = (Boolean) $boolean; + } + + /** + * Sets whether false is allowed as value indicating that the array should + * be unset. + * + * @param Boolean $allow + * @return void + */ + public function setAllowFalse($allow) + { + $this->allowFalse = (Boolean) $allow; + } + + /** + * Sets whether new keys can be defined in subsequent configurations. + * + * @param Boolean $allow + * @return void + */ + public function setAllowNewKeys($allow) + { + $this->allowNewKeys = (Boolean) $allow; + } + + public function setPerformDeepMerging($boolean) + { + $this->performDeepMerging = (Boolean) $boolean; } public function setName($name) @@ -26,10 +120,41 @@ public function setName($name) $this->name = $name; } + public function hasDefaultValue() + { + if (null !== $this->prototype) { + return true; + } + + return $this->addIfNotSet; + } + + public function getDefaultValue() + { + if (!$this->hasDefaultValue()) { + throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); + } + + if (null !== $this->prototype) { + return array(); + } + + $defaults = array(); + foreach ($this->children as $name => $child) { + if (!$child->hasDefaultValue()) { + continue; + } + + $defaults[$name] = $child->getDefaultValue(); + } + + return $defaults; + } + public function setPrototype(PrototypeNodeInterface $node) { if (count($this->children) > 0) { - throw new \RuntimeException('An ARRAY node must either have concrete children, or a prototype node.'); + throw new \RuntimeException($this->getPath().': An ARRAY node must either have concrete children, or a prototype node.'); } $this->prototype = $node; @@ -51,9 +176,65 @@ public function addChild(NodeInterface $node) $this->children[$name] = $node; } + protected function finalizeValue($value) + { + if (false === $value) { + throw new UnsetKeyException(sprintf( + 'Unsetting key for path "%s", value: %s', + $this->getPath(), + json_encode($value) + )); + } + + if (null !== $this->prototype) { + foreach ($value as $k => $v) { + try { + $value[$k] = $this->prototype->finalize($v); + } catch (UnsetKeyException $unset) { + unset($value[$k]); + } + } + + if (count($value) < $this->minNumberOfElements) { + throw new InvalidConfigurationException(sprintf( + 'You must define at least %d element(s) for path "%s".', + $this->minNumberOfElements, + $this->getPath() + )); + } + + return $value; + } + + foreach ($this->children as $name => $child) { + if (!array_key_exists($name, $value)) { + if ($child->isRequired()) { + throw new InvalidConfigurationException(sprintf( + 'The node at path "%s" must be configured.', + $this->getPath() + )); + } + + if ($child->hasDefaultValue()) { + $value[$name] = $child->getDefaultValue(); + } + + continue; + } + + try { + $value[$name] = $child->finalize($value[$name]); + } catch (UnsetKeyException $unset) { + unset($value[$name]); + } + } + + return $value; + } + protected function validateType($value) { - if (!is_array($value)) { + if (!is_array($value) && (!$this->allowFalse || false !== $value)) { throw new InvalidTypeException(sprintf( 'Invalid type for path "%s". Expected array, but got %s', $this->getPath(), @@ -64,7 +245,11 @@ protected function validateType($value) protected function normalizeValue($value) { - foreach ($this->normalizeTransformations as $transformation) { + if (false === $value) { + return $value; + } + + foreach ($this->xmlRemappings as $transformation) { list($singular, $plural) = $transformation; if (!isset($value[$singular])) { @@ -77,8 +262,24 @@ protected function normalizeValue($value) if (null !== $this->prototype) { $normalized = array(); foreach ($value as $k => $v) { - if (null !== $this->keyAttribute && is_array($v) && isset($v[$this->keyAttribute])) { - $k = $v[$this->keyAttribute]; + if (null !== $this->keyAttribute && is_array($v)) { + if (!isset($v[$this->keyAttribute]) && is_int($k)) { + throw new InvalidConfigurationException(sprintf( + 'You must set a "%s" attribute for path "%s".', + $this->keyAttribute, + $this->getPath() + )); + } else if (isset($v[$this->keyAttribute])) { + $k = $v[$this->keyAttribute]; + } + + if (array_key_exists($k, $normalized)) { + throw new DuplicateKeyException(sprintf( + 'Duplicate key "%s" for path "%s".', + $k, + $this->getPath() + )); + } } $this->prototype->setName($k); @@ -103,4 +304,56 @@ protected function normalizeValue($value) return $normalized; } -} + + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // prototype, and key is irrelevant, so simply append the element + if (null !== $this->prototype && null === $this->keyAttribute) { + $leftSide[] = $v; + continue; + } + + // no conflict + if (!array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + throw new InvalidConfigurationException(sprintf( + 'You are not allowed to define new elements for path "%s". ' + .'Please define all elements for this path in one config file.', + $this->getPath() + )); + } + + $leftSide[$k] = $v; + continue; + } + + try { + if (null !== $this->prototype) { + $this->prototype->setName($k); + $leftSide[$k] = $this->prototype->merge($leftSide[$k], $v); + } else { + if (!isset($this->children[$k])) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); + } + } catch (UnsetKeyException $unset) { + unset($leftSide[$k]); + } + } + + return $leftSide; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/BaseNode.php b/src/Symfony/Component/DependencyInjection/Configuration/BaseNode.php index 69f5847fbcd74..b2a78e9e910d5 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/BaseNode.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/BaseNode.php @@ -2,15 +2,25 @@ namespace Symfony\Component\DependencyInjection\Configuration; +use Symfony\Component\DependencyInjection\Configuration\Exception\Exception; +use Symfony\Component\DependencyInjection\Configuration\Exception\ForbiddenOverwriteException; + +/** + * The base node class + * + * @author Johannes M. Schmitt + */ abstract class BaseNode implements NodeInterface { protected $name; protected $parent; - protected $beforeTransformations; - protected $afterTransformations; - protected $nodeFactory; + protected $normalizationClosures; + protected $finalValidationClosures; + protected $allowOverwrite; + protected $required; + protected $equivalentValues; - public function __construct($name, NodeInterface $parent = null, $beforeTransformations = array(), $afterTransformations = array()) + public function __construct($name, NodeInterface $parent = null) { if (false !== strpos($name, '.')) { throw new \InvalidArgumentException('The name must not contain ".".'); @@ -18,8 +28,41 @@ public function __construct($name, NodeInterface $parent = null, $beforeTransfor $this->name = $name; $this->parent = $parent; - $this->beforeTransformations = $beforeTransformations; - $this->afterTransformations = $afterTransformations; + $this->normalizationClosures = array(); + $this->finalValidationClosures = array(); + $this->allowOverwrite = true; + $this->required = false; + $this->equivalentValues = array(); + } + + public function addEquivalentValue($originalValue, $equivalentValue) + { + $this->equivalentValues[] = array($originalValue, $equivalentValue); + } + + public function setRequired($boolean) + { + $this->required = (Boolean) $boolean; + } + + public function setAllowOverwrite($allow) + { + $this->allowOverwrite = (Boolean) $allow; + } + + public function setNormalizationClosures(array $closures) + { + $this->normalizationClosures = $closures; + } + + public function setFinalValidationClosures(array $closures) + { + $this->finalValidationClosures = $closures; + } + + public function isRequired() + { + return $this->required; } public function getName() @@ -38,22 +81,64 @@ public function getPath() return $path; } + public final function merge($leftSide, $rightSide) + { + if (!$this->allowOverwrite) { + throw new ForbiddenOverwriteException(sprintf( + 'Configuration path "%s" cannot be overwritten. You have to ' + .'define all options for this path, and any of its sub-paths in ' + .'one configuration section.', + $this->getPath() + )); + } + + $this->validateType($leftSide); + $this->validateType($rightSide); + + return $this->mergeValues($leftSide, $rightSide); + } + public final function normalize($value) { - // run before transformations - foreach ($this->beforeTransformations as $transformation) { - $value = $transformation($value); + // run custom normalization closures + foreach ($this->normalizationClosures as $closure) { + $value = $closure($value); + } + + // replace value with their equivalent + foreach ($this->equivalentValues as $data) { + if ($data[0] === $value) { + $value = $data[1]; + } } // validate type $this->validateType($value); // normalize value - $value = $this->normalizeValue($value); + return $this->normalizeValue($value); + } + + public final function finalize($value) + { + $this->validateType($value); + + $value = $this->finalizeValue($value); - // run after transformations - foreach ($this->afterTransformations as $transformation) { - $value = $transformation($value); + // Perform validation on the final value if a closure has been set. + // The closure is also allowed to return another value. + foreach ($this->finalValidationClosures as $closure) { + try { + $value = $closure($value); + } catch (Exception $correctEx) { + throw $correctEx; + } catch (\Exception $invalid) { + throw new InvalidConfigurationException(sprintf( + 'Invalid configuration for path "%s": %s', + $this->getPath(), + $invalid->getMessage() + ), $invalid->getCode(), $invalid); + } } return $value; @@ -61,4 +146,6 @@ public final function normalize($value) abstract protected function validateType($value); abstract protected function normalizeValue($value); + abstract protected function mergeValues($leftSide, $rightSide); + abstract protected function finalizeValue($value); } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/BooleanNode.php b/src/Symfony/Component/DependencyInjection/Configuration/BooleanNode.php new file mode 100644 index 0000000000000..3960e0e756b5c --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/BooleanNode.php @@ -0,0 +1,21 @@ +getPath(), + json_encode($value) + )); + } + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Builder/ExprBuilder.php b/src/Symfony/Component/DependencyInjection/Configuration/Builder/ExprBuilder.php index c8f2e0cbdd848..1e29a6ed72778 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/Builder/ExprBuilder.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/Builder/ExprBuilder.php @@ -2,6 +2,11 @@ namespace Symfony\Component\DependencyInjection\Configuration\Builder; +/** + * This class builds an if expression. + * + * @author Johannes M. Schmitt + */ class ExprBuilder { public $parent; @@ -13,8 +18,12 @@ public function __construct($parent) $this->parent = $parent; } - public function ifTrue(\Closure $closure) + public function ifTrue(\Closure $closure = null) { + if (null === $closure) { + $closure = function($v) { return true === $v; }; + } + $this->ifPart = $closure; return $this; @@ -48,6 +57,31 @@ public function then(\Closure $closure) return $this; } + public function thenReplaceKeyWithAttribute($attribute) + { + $this->thenPart = function($v) { + $newValue = array(); + foreach ($v as $k => $oldValue) { + if (is_array($oldValue) && isset($oldValue['id'])) { + $k = $oldValue['id']; + } + + $newValue[$k] = $oldValue; + } + + return $newValue; + }; + + return $this; + } + + public function thenEmptyArray() + { + $this->thenPart = function($v) { return array(); }; + + return $this; + } + public function end() { if (null === $this->ifPart) { diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Builder/MergeBuilder.php b/src/Symfony/Component/DependencyInjection/Configuration/Builder/MergeBuilder.php new file mode 100644 index 0000000000000..5ac10001dbd44 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Builder/MergeBuilder.php @@ -0,0 +1,41 @@ + + */ +class MergeBuilder +{ + public $parent; + public $allowFalse; + public $allowOverwrite; + + public function __construct($parent) + { + $this->parent = $parent; + $this->allowFalse = false; + $this->allowOverwrite = true; + } + + public function allowUnset($allow = true) + { + $this->allowFalse = $allow; + + return $this; + } + + public function denyOverwrite($deny = true) + { + $this->allowOverwrite = !$deny; + + return $this; + } + + public function end() + { + return $this->parent; + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Builder/NodeBuilder.php b/src/Symfony/Component/DependencyInjection/Configuration/Builder/NodeBuilder.php index 0c63068d71433..aa19de0e61139 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/Builder/NodeBuilder.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/Builder/NodeBuilder.php @@ -2,6 +2,11 @@ namespace Symfony\Component\DependencyInjection\Configuration\Builder; +/** + * This class provides a fluent interface for building a config tree. + * + * @author Johannes M. Schmitt + */ class NodeBuilder { /************ @@ -13,9 +18,20 @@ class NodeBuilder public $parent; public $children; public $prototype; - public $normalizeTransformations; - public $beforeTransformations; - public $afterTransformations; + public $normalization; + public $merge; + public $finalization; + public $defaultValue; + public $default; + public $addDefaults; + public $required; + public $atLeastOne; + public $allowNewKeys; + public $allowEmptyValue; + public $nullEquivalent; + public $trueEquivalent; + public $falseEquivalent; + public $performDeepMerging; public function __construct($name, $type, $parent = null) { @@ -23,10 +39,28 @@ public function __construct($name, $type, $parent = null) $this->type = $type; $this->parent = $parent; - $this->children = - $this->beforeTransformations = - $this->afterTransformations = - $this->normalizeTransformations = array(); + $this->default = false; + $this->required = false; + $this->addDefaults = false; + $this->allowNewKeys = true; + $this->atLeastOne = false; + $this->allowEmptyValue = true; + $this->children = array(); + $this->performDeepMerging = true; + + if ('boolean' === $type) { + $this->nullEquivalent = true; + } else if ('array' === $type) { + $this->nullEquivalent = array(); + } + + if ('array' === $type) { + $this->trueEquivalent = array(); + } else { + $this->trueEquivalent = true; + } + + $this->falseEquivalent = false; } /**************************** @@ -35,54 +69,178 @@ public function __construct($name, $type, $parent = null) public function node($name, $type) { - $node = new NodeBuilder($name, $type, $this); + $node = new static($name, $type, $this); return $this->children[$name] = $node; } - public function normalize($key, $plural = null) + public function arrayNode($name) { - if (null === $plural) { - $plural = $key.'s'; - } + return $this->node($name, 'array'); + } + + public function scalarNode($name) + { + return $this->node($name, 'scalar'); + } + + public function booleanNode($name) + { + return $this->node($name, 'boolean'); + } - $this->normalizeTransformations[] = array($key, $plural); + public function defaultValue($value) + { + $this->default = true; + $this->defaultValue = $value; return $this; } - public function key($name) + public function isRequired() { - $this->key = $name; + $this->required = true; + + return $this; + } + + public function containsNameValuePairsWithKeyAttribute($attribute) + { + $this->beforeNormalization() + ->ifArray() + ->thenReplaceKeyWithAttribute($attribute) + ; + + $this->useAttributeAsKey($attribute); return $this; } - public function before(\Closure $closure = null) + public function requiresAtLeastOneElement() { - if (null !== $closure) { - $this->beforeTransformations[] = $closure; + $this->atLeastOne = true; + + return $this; + } + + public function treatNullLike($value) + { + $this->nullEquivalent = $value; + + return $this; + } + + public function treatTrueLike($value) + { + $this->trueEquivalent = $value; + + return $this; + } + + public function treatFalseLike($value) + { + $this->falseEquivalent = $value; + + return $this; + } + + public function defaultNull() + { + return $this->defaultValue(null); + } + + public function defaultTrue() + { + return $this->defaultValue(true); + } + + public function defaultFalse() + { + return $this->defaultValue(false); + } - return $this; + public function addDefaultsIfNotSet() + { + $this->addDefaults = true; + + return $this; + } + + public function disallowNewKeysInSubsequentConfigs() + { + $this->allowNewKeys = false; + + return $this; + } + + protected function normalization() + { + if (null === $this->normalization) { + $this->normalization = new NormalizationBuilder($this); } - return $this->beforeTransformations[] = new ExprBuilder($this); + return $this->normalization; } - public function prototype($type) + public function beforeNormalization() { - return $this->prototype = new NodeBuilder(null, $type, $this); + return $this->normalization()->before(); } - public function after(\Closure $closure = null) + public function fixXmlConfig($singular, $plural = null) { - if (null !== $closure) { - $this->afterTransformations[] = $closure; + $this->normalization()->remap($singular, $plural); - return $this; + return $this; + } + + public function useAttributeAsKey($name) + { + $this->key = $name; + + return $this; + } + + protected function merge() + { + if (null === $this->merge) { + $this->merge = new MergeBuilder($this); } - return $this->afterTransformations[] = new ExprBuilder($this); + return $this->merge; + } + + public function cannotBeOverwritten($deny = true) + { + $this->merge()->denyOverwrite($deny); + + return $this; + } + + public function cannotBeEmpty() + { + $this->allowEmptyValue = false; + + return $this; + } + + public function canBeUnset($allow = true) + { + $this->merge()->allowUnset($allow); + + return $this; + } + + public function prototype($type) + { + return $this->prototype = new static(null, $type, $this); + } + + public function performNoDeepMerging() + { + $this->performDeepMerging = false; + + return $this; } public function end() diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Builder/NormalizationBuilder.php b/src/Symfony/Component/DependencyInjection/Configuration/Builder/NormalizationBuilder.php new file mode 100644 index 0000000000000..96e76cb586d57 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Builder/NormalizationBuilder.php @@ -0,0 +1,48 @@ + + */ +class NormalizationBuilder +{ + public $parent; + public $before; + public $remappings; + + public function __construct($parent) + { + $this->parent = $parent; + + $this->keys = false; + + $this->remappings = + $this->before = + $this->after = array(); + } + + public function remap($key, $plural = null) + { + if (null === $plural) { + $plural = $key.'s'; + } + + $this->remappings[] = array($key, $plural); + + return $this; + } + + public function before(\Closure $closure = null) + { + if (null !== $closure) { + $this->before[] = $closure; + + return $this; + } + + return $this->before[] = new ExprBuilder($this->parent); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Builder/TreeBuilder.php b/src/Symfony/Component/DependencyInjection/Configuration/Builder/TreeBuilder.php index 93f48bdf1936b..e24a23cb4dce5 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/Builder/TreeBuilder.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/Builder/TreeBuilder.php @@ -2,9 +2,18 @@ namespace Symfony\Component\DependencyInjection\Configuration\Builder; +use Symfony\Component\DependencyInjection\Configuration\BaseNode; + +use Symfony\Component\DependencyInjection\Configuration\BooleanNode; + use Symfony\Component\DependencyInjection\Configuration\ArrayNode; use Symfony\Component\DependencyInjection\Configuration\ScalarNode; +/** + * This is the entry class for building your own config tree. + * + * @author Johannes M. Schmitt + */ class TreeBuilder { protected $root; @@ -32,9 +41,6 @@ public function buildTree() protected function createConfigNode(NodeBuilder $node) { - $node->beforeTransformations = $this->buildExpressions($node->beforeTransformations); - $node->afterTransformations = $this->buildExpressions($node->afterTransformations); - $method = 'create'.$node->type.'ConfigNode'; if (!method_exists($this, $method)) { throw new \RuntimeException(sprintf('Unknown node type: "%s"', $node->type)); @@ -43,14 +49,77 @@ protected function createConfigNode(NodeBuilder $node) return $this->$method($node); } + protected function createBooleanConfigNode(NodeBuilder $node) + { + $configNode = new BooleanNode($node->name, $node->parent); + $this->configureScalarNode($configNode, $node); + + return $configNode; + } + protected function createScalarConfigNode(NodeBuilder $node) { - return new ScalarNode($node->name, $node->parent, $node->beforeTransformations, $node->afterTransformations); + $configNode = new ScalarNode($node->name, $node->parent); + $this->configureScalarNode($configNode, $node); + + return $configNode; + } + + protected function configureScalarNode(ScalarNode $configNode, NodeBuilder $node) + { + if (null !== $node->normalization) { + $configNode->setNormalizationClosures( + $this->buildExpressions($node->normalization->before) + ); + } + + if (null !== $node->merge) { + $configNode->setAllowOverwrite($node->merge->allowOverwrite); + } + + if (true === $node->default) { + $configNode->setDefaultValue($node->defaultValue); + } + + if (false === $node->allowEmptyValue) { + $configNode->setAllowEmptyValue($node->allowEmptyValue); + } + + $configNode->addEquivalentValue(null, $node->nullEquivalent); + $configNode->addEquivalentValue(true, $node->trueEquivalent); + $configNode->addEquivalentValue(false, $node->falseEquivalent); } protected function createArrayConfigNode(NodeBuilder $node) { - $configNode = new ArrayNode($node->name, $node->parent, $node->beforeTransformations, $node->afterTransformations, $node->normalizeTransformations, $node->key); + $configNode = new ArrayNode($node->name, $node->parent); + $configNode->setAddIfNotSet($node->addDefaults); + $configNode->setAllowNewKeys($node->allowNewKeys); + $configNode->addEquivalentValue(null, $node->nullEquivalent); + $configNode->addEquivalentValue(true, $node->trueEquivalent); + $configNode->addEquivalentValue(false, $node->falseEquivalent); + $configNode->setPerformDeepMerging($node->performDeepMerging); + + if (null !== $node->key) { + $configNode->setKeyAttribute($node->key); + } + + if (true === $node->atLeastOne) { + $configNode->setMinNumberOfElements(1); + } + + if (null !== $node->normalization) { + $configNode->setNormalizationClosures( + $this->buildExpressions($node->normalization->before) + ); + + $configNode->setXmlRemappings($node->normalization->remappings); + } + + if (null !== $node->merge) { + $configNode->setAllowOverwrite($node->merge->allowOverwrite); + $configNode->setAllowFalse($node->merge->allowFalse); + } foreach ($node->children as $child) { $child->parent = $configNode; diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Exception/DuplicateKeyException.php b/src/Symfony/Component/DependencyInjection/Configuration/Exception/DuplicateKeyException.php new file mode 100644 index 0000000000000..7da500ba5693f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Exception/DuplicateKeyException.php @@ -0,0 +1,13 @@ + + */ +class DuplicateKeyException extends InvalidConfigurationException +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Exception/Exception.php b/src/Symfony/Component/DependencyInjection/Configuration/Exception/Exception.php index e5a464b27694d..c669089f472b4 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/Exception/Exception.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/Exception/Exception.php @@ -2,6 +2,11 @@ namespace Symfony\Component\DependencyInjection\Configuration\Exception; +/** + * Base exception for all configuration exceptions + * + * @author Johannes M. Schmitt + */ class Exception extends \RuntimeException { } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Exception/ForbiddenOverwriteException.php b/src/Symfony/Component/DependencyInjection/Configuration/Exception/ForbiddenOverwriteException.php new file mode 100644 index 0000000000000..0f7537747ec31 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Exception/ForbiddenOverwriteException.php @@ -0,0 +1,13 @@ + + */ +class ForbiddenOverwriteException extends InvalidConfigurationException +{ +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidConfigurationException.php b/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidConfigurationException.php new file mode 100644 index 0000000000000..71f3ffbd5282e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidConfigurationException.php @@ -0,0 +1,13 @@ + + */ +class InvalidConfigurationException extends Exception +{ +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidTypeException.php b/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidTypeException.php index 436c80fa80add..3cdbc52439c58 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidTypeException.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/Exception/InvalidTypeException.php @@ -5,8 +5,8 @@ /** * This exception is thrown if an invalid type is encountered. * - * @author johannes + * @author Johannes M. Schmitt */ -class InvalidTypeException extends Exception +class InvalidTypeException extends InvalidConfigurationException { } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Exception/UnsetKeyException.php b/src/Symfony/Component/DependencyInjection/Configuration/Exception/UnsetKeyException.php new file mode 100644 index 0000000000000..2388b134b42bb --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Exception/UnsetKeyException.php @@ -0,0 +1,13 @@ + + */ +class UnsetKeyException extends Exception +{ +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/NodeInterface.php b/src/Symfony/Component/DependencyInjection/Configuration/NodeInterface.php index a5e8611c637d1..70271946b46b5 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/NodeInterface.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/NodeInterface.php @@ -2,9 +2,21 @@ namespace Symfony\Component\DependencyInjection\Configuration; +/** + * Common Interface among all nodes. + * + * In most cases, it is better to inherit from BaseNode instead of implementing + * this interface yourself. + * + * @author Johannes M. Schmitt + */ interface NodeInterface { function getName(); function getPath(); + function isRequired(); + function hasDefaultValue(); + function getDefaultValue(); function normalize($value); + function merge($leftSide, $rightSide); } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/Processor.php b/src/Symfony/Component/DependencyInjection/Configuration/Processor.php new file mode 100644 index 0000000000000..cdabd29a030fe --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Configuration/Processor.php @@ -0,0 +1,26 @@ + + */ +class Processor +{ + public function process(NodeInterface $configTree, array $configs) + { + $configs = Extension::normalizeKeys($configs); + + $currentConfig = array(); + foreach ($configs as $config) { + $config = $configTree->normalize($config); + $currentConfig = $configTree->merge($currentConfig, $config); + } + + return $configTree->finalize($currentConfig); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/Configuration/ScalarNode.php b/src/Symfony/Component/DependencyInjection/Configuration/ScalarNode.php index cdcebe3dac6a7..3f270bf14cf24 100644 --- a/src/Symfony/Component/DependencyInjection/Configuration/ScalarNode.php +++ b/src/Symfony/Component/DependencyInjection/Configuration/ScalarNode.php @@ -2,10 +2,48 @@ namespace Symfony\Component\DependencyInjection\Configuration; +use Symfony\Component\DependencyInjection\Configuration\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\Configuration\Exception\InvalidTypeException; +/** + * This node represents a scalar value in the config tree. + * + * The following values are considered scalars: + * * booleans + * * strings + * * null + * * integers + * * floats + * + * @author Johannes M. Schmitt + */ class ScalarNode extends BaseNode implements PrototypeNodeInterface { + protected $defaultValueSet = false; + protected $defaultValue; + protected $allowEmptyValue = true; + + public function setDefaultValue($value) + { + $this->defaultValueSet = true; + $this->defaultValue = $value; + } + + public function hasDefaultValue() + { + return $this->defaultValueSet; + } + + public function getDefaultValue() + { + return $this->defaultValue; + } + + public function setAllowEmptyValue($boolean) + { + $this->allowEmptyValue = (Boolean) $boolean; + } + public function setName($name) { $this->name = $name; @@ -13,8 +51,8 @@ public function setName($name) protected function validateType($value) { - if (!is_scalar($value)) { - throw new \InvalidTypeException(sprintf( + if (!is_scalar($value) && null !== $value) { + throw new InvalidTypeException(sprintf( 'Invalid type for path "%s". Expected scalar, but got %s.', $this->getPath(), json_encode($value) @@ -22,8 +60,26 @@ protected function validateType($value) } } + protected function finalizeValue($value) + { + if (!$this->allowEmptyValue && empty($value)) { + throw new InvalidConfigurationException(sprintf( + 'The path "%s" cannot contain an empty value, but got %s.', + $this->getPath(), + json_encode($value) + )); + } + + return $value; + } + protected function normalizeValue($value) { return $value; } + + protected function mergeValues($leftSide, $rightSide) + { + return $rightSide; + } } \ No newline at end of file diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 8bf948e67b9e4..008ef416c269e 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -695,10 +695,12 @@ protected function createService(Definition $definition, $id) $arguments = $this->resolveServices($this->getParameterBag()->resolveValue($definition->getArguments())); if (null !== $definition->getFactoryMethod()) { - if (null !== $definition->getFactoryService()) { + if (null !== $definition->getFactoryClass()) { + $factory = $this->getParameterBag()->resolveValue($definition->getFactoryClass()); + } elseif (null !== $definition->getFactoryService()) { $factory = $this->get($this->getParameterBag()->resolveValue($definition->getFactoryService())); } else { - $factory = $this->getParameterBag()->resolveValue($definition->getClass()); + throw new \RuntimeException('Cannot create service from factory method without a factory service or factory class.'); } $service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments); diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index fa70eb6d14e55..e3d58cb6f2080 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -20,6 +20,7 @@ class Definition { protected $class; protected $file; + protected $factoryClass; protected $factoryMethod; protected $factoryService; protected $scope; @@ -49,16 +50,41 @@ public function __construct($class = null, array $arguments = array()) $this->abstract = false; } + /** + * Sets the name of the class that acts as a factory using the factory method, + * which will be invoked statically. + * + * @param string $factoryClass The factory class name + * + * @return Definition The current instance + */ + public function setFactoryClass($factoryClass) + { + $this->factoryClass = $factoryClass; + + return $this; + } + + /** + * Gets the factory class. + * + * @return string The factory class name + */ + public function getFactoryClass() + { + return $this->factoryClass; + } + /** * Sets the factory method able to create an instance of this class. * - * @param string $method The method name + * @param string $factoryMethod The factory method name * * @return Definition The current instance */ - public function setFactoryMethod($method) + public function setFactoryMethod($factoryMethod) { - $this->factoryMethod = $method; + $this->factoryMethod = $factoryMethod; return $this; } @@ -74,7 +100,7 @@ public function getFactoryMethod() } /** - * Sets the name of the service that acts as a factory using the constructor method. + * Sets the name of the service that acts as a factory using the factory method. * * @param string $factoryService The factory service id * diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php index cde424c50bf18..e74a1bf7e319c 100644 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -37,11 +37,11 @@ public function setClass($class) return parent::setClass($class); } - public function setFactoryService($service) + public function setFactoryClass($class) { - $this->changes['factory_service'] = true; + $this->changes['factory_class'] = true; - return parent::setFactoryService($service); + return parent::setFactoryClass($class); } public function setFactoryMethod($method) @@ -51,6 +51,13 @@ public function setFactoryMethod($method) return parent::setFactoryMethod($method); } + public function setFactoryService($service) + { + $this->changes['factory_service'] = true; + + return parent::setFactoryService($service); + } + public function setConfigurator($callable) { $this->changes['configurator'] = true; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 57e984f408bb9..7829b10b1a5fa 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -230,10 +230,12 @@ protected function addServiceInlinedDefinitions($id, $definition) } if (null !== $sDefinition->getFactoryMethod()) { - if (null !== $sDefinition->getFactoryService()) { + if (null !== $sDefinition->getFactoryClass()) { + $code .= sprintf(" \$%s = call_user_func(array(%s, '%s')%s);\n", $name, $this->dumpValue($sDefinition->getFactoryClass()), $sDefinition->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + } elseif (null !== $sDefinition->getFactoryService()) { $code .= sprintf(" \$%s = %s->%s(%s);\n", $name, $this->getServiceCall($sDefinition->getFactoryService()), $sDefinition->getFactoryMethod(), implode(', ', $arguments)); } else { - $code .= sprintf(" \$%s = call_user_func(array(%s, '%s')%s);\n", $name, $class, $sDefinition->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + throw new \RuntimeException('Factory service or factory class must be defined in service definition for '.$id); } } elseif (false !== strpos($class, '$')) { $code .= sprintf(" \$class = %s;\n \$%s = new \$class(%s);\n", $class, $name, implode(', ', $arguments)); @@ -294,10 +296,12 @@ protected function addServiceInstance($id, $definition) } if (null !== $definition->getFactoryMethod()) { - if (null !== $definition->getFactoryService()) { + if (null !== $definition->getFactoryClass()) { + $code = sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass()), $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : ''); + } elseif (null !== $definition->getFactoryService()) { $code = sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService()), $definition->getFactoryMethod(), implode(', ', $arguments)); } else { - $code = sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $class, $definition->getFactoryMethod(), $arguments ? ', '.implode(', ', $arguments) : ''); + throw new \RuntimeException('Factory method requires a factory service or factory class in service definition for '.$id); } } elseif (false !== strpos($class, '$')) { $code = sprintf(" \$class = %s;\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments)); @@ -404,8 +408,10 @@ protected function addService($id, $definition) $return = ''; if ($definition->isSynthetic()) { $return = sprintf('@throws \RuntimeException always since this service is expected to be injected dynamically'); - } else if ($class = $definition->getClass()) { + } elseif ($class = $definition->getClass()) { $return = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'Object' : $class, $class); + } elseif ($definition->getFactoryClass()) { + $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod()); } elseif ($definition->getFactoryService()) { $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod()); } @@ -821,10 +827,12 @@ protected function dumpValue($value, $interpolate = true) } if (null !== $value->getFactoryMethod()) { - if (null !== $value->getFactoryService()) { + if (null !== $value->getFactoryClass()) { + return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + } elseif (null !== $value->getFactoryService()) { return sprintf("%s->%s(%s)", $this->getServiceCall($value->getFactoryService()), $value->getFactoryMethod(), implode(', ', $arguments)); } else { - return sprintf("call_user_func(array(%s, '%s')%s)", $class, $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + throw new \RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.'); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index ec0a10625ee25..ec39573f83b72 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -143,7 +143,7 @@ protected function parseDefinition($id, $service, $file) $definition = new Definition(); } - foreach (array('class', 'scope', 'public', 'factory-method', 'factory-service', 'synthetic', 'abstract') as $key) { + foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'abstract') as $key) { if (isset($service[$key])) { $method = 'set'.str_replace('-', '', $key); $definition->$method((string) $service->getAttributeAsPhp($key)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 65def0455f834..926a274a00300 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -165,6 +165,10 @@ protected function parseDefinition($id, $service, $file) $definition->setAbstract($service['abstract']); } + if (isset($service['factory_class'])) { + $definition->setFactoryClass($service['factory_class']); + } + if (isset($service['factory_method'])) { $definition->setFactoryMethod($service['factory_method']); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index a6ccf726ee44f..35a8a684c9887 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -105,6 +105,7 @@ + diff --git a/src/Symfony/Component/Form/DateField.php b/src/Symfony/Component/Form/DateField.php index 3330bad81f765..f08c4733e976f 100644 --- a/src/Symfony/Component/Form/DateField.php +++ b/src/Symfony/Component/Form/DateField.php @@ -89,6 +89,19 @@ class DateField extends HybridField */ protected $formatter; + /** + * {@inheritDoc} + */ + public function __construct($key, array $options = array()) + { + // Override parent option + // \DateTime objects are never edited by reference, because + // we treat them like value objects + $this->addOption('by_reference', false); + + parent::__construct($key, $options); + } + protected function configure() { $this->addOption('widget', self::CHOICE, self::$widgets); diff --git a/src/Symfony/Component/Form/DateTimeField.php b/src/Symfony/Component/Form/DateTimeField.php index 3002b7f2bfb7c..5f9d96ed8d13e 100644 --- a/src/Symfony/Component/Form/DateTimeField.php +++ b/src/Symfony/Component/Form/DateTimeField.php @@ -75,6 +75,19 @@ class DateTimeField extends Form TimeField::INPUT, ); + /** + * {@inheritDoc} + */ + public function __construct($key, array $options = array()) + { + // Override parent option + // \DateTime objects are never edited by reference, because + // we treat them like value objects + $this->addOption('by_reference', false); + + parent::__construct($key, $options); + } + protected function configure() { $this->addOption('date_widget', DateField::CHOICE, self::$dateWidgets); diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 820772bd1bbd3..1cd9f7ff262fb 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -58,6 +58,12 @@ class Form extends Field implements \IteratorAggregate, FormInterface */ protected $dataClass; + /** + * Stores the constructor closure for creating new domain object instances + * @var \Closure + */ + protected $dataConstructor; + /** * The context used when creating the form * @var FormContext @@ -86,6 +92,7 @@ public static function create(FormContextInterface $context, $name = null, array public function __construct($name = null, array $options = array()) { $this->addOption('data_class'); + $this->addOption('data_constructor'); $this->addOption('csrf_field_name', '_token'); $this->addOption('csrf_provider'); $this->addOption('field_factory'); @@ -93,6 +100,7 @@ public function __construct($name = null, array $options = array()) $this->addOption('virtual', false); $this->addOption('validator'); $this->addOption('context'); + $this->addOption('by_reference', true); if (isset($options['validation_groups'])) { $options['validation_groups'] = (array)$options['validation_groups']; @@ -102,6 +110,10 @@ public function __construct($name = null, array $options = array()) $this->dataClass = $options['data_class']; } + if (isset($options['data_constructor'])) { + $this->dataConstructor = $options['data_constructor']; + } + parent::__construct($name, $options); // Enable CSRF protection @@ -340,6 +352,16 @@ protected function getFieldsByVisibility($hidden, $recursive) */ public function setData($data) { + if (empty($data)) { + if ($this->dataConstructor) { + $constructor = $this->dataConstructor; + $data = $constructor(); + } else if ($this->dataClass) { + $class = $this->dataClass; + $data = new $class(); + } + } + parent::setData($data); // get transformed data and pass its values to child fields @@ -712,6 +734,10 @@ public function getCsrfProvider() */ public function bind(Request $request, $data = null) { + if (!$this->getName()) { + throw new FormException('You cannot bind anonymous forms. Please give this form a name'); + } + // Store object from which to read the default values and where to // write the submitted values if (null !== $data) { @@ -880,6 +906,21 @@ public function validateData(ExecutionContext $context) } } + /** + * {@inheritDoc} + */ + public function writeProperty(&$objectOrArray) + { + $data = $this->getData(); + + // Don't update parent if data is a composite type (object or array) + // and "by_reference" option is true, because then we expect that + // we are working with a reference to the parent's data + if (!is_object($data) || !is_object($objectOrArray) || !$this->getOption('by_reference')) { + parent::writeProperty($objectOrArray); + } + } + /** * Merges two arrays without reindexing numeric keys. * diff --git a/src/Symfony/Component/Form/TimeField.php b/src/Symfony/Component/Form/TimeField.php index 3f221db2b4bf3..a8961fe7aa830 100644 --- a/src/Symfony/Component/Form/TimeField.php +++ b/src/Symfony/Component/Form/TimeField.php @@ -59,6 +59,19 @@ class TimeField extends Form self::RAW, ); + /** + * {@inheritDoc} + */ + public function __construct($key, array $options = array()) + { + // Override parent option + // \DateTime objects are never edited by reference, because + // we treat them like value objects + $this->addOption('by_reference', false); + + parent::__construct($key, $options); + } + /** * {@inheritDoc} */ diff --git a/src/Symfony/Component/Form/ValueTransformer/BooleanToStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/BooleanToStringTransformer.php index 1a793f5fce149..890b0bc326bfa 100644 --- a/src/Symfony/Component/Form/ValueTransformer/BooleanToStringTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/BooleanToStringTransformer.php @@ -47,7 +47,7 @@ public function transform($value) * @param string $value String value. * @return Boolean Boolean value. */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { if (!is_string($value)) { throw new UnexpectedTypeException($value, 'string'); diff --git a/src/Symfony/Component/Form/ValueTransformer/CollectionToStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/CollectionToStringTransformer.php deleted file mode 100644 index b939f69236dda..0000000000000 --- a/src/Symfony/Component/Form/ValueTransformer/CollectionToStringTransformer.php +++ /dev/null @@ -1,157 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\ValueTransformer; - -use Symfony\Component\Form\Configurable; -use Doctrine\Common\Collections\Collection; - -/** - * Transforms an instance of Doctrine\Common\Collections\Collection into a string of unique names. - * - * Use-Cases for this transformer include: List of Tag-Names, List Of Group/User-Names or the like. - * - * This transformer only makes sense if you know the list of related collections to be small and - * that they have a unique identifier field that is of meaning to the user (Tag Names) and is - * enforced to be unique in the storage. - * - * This transformer can cause the following SQL operations to happen in the case of an ORM collection: - * 1. Initialize the whole collection using one SELECT query - * 2. For each removed element issue an UPDATE or DELETE stmt (depending on one-to-many or many-to-many) - * 3. For each inserted element issue an INSERT or UPDATE stmt (depending on one-to-many or many-to-many) - * 4. Extra updates if necessary by the ORM. - * - * @todo Refactor to make 'fieldName' optional (identifier). - * - * @author Benjamin Eberlei - * @author Bernhard Schussek - */ -class CollectionToStringTransformer extends Configurable implements ValueTransformerInterface -{ - protected function configure() - { - $this->addOption('trim', true); - $this->addOption('separator', ','); - $this->addOption('explode_callback', 'explode'); - $this->addOption('implode_callback', 'implode'); - $this->addOption('create_instance_callback', null); - $this->addRequiredOption('em'); - $this->addRequiredOption('class_name'); - $this->addRequiredOption('field_name'); - - parent::configure(); - } - - /** - * @param string $value - * @param Collection $collection - */ - public function reverseTransform($value, $collection) - { - if (strlen(trim($value)) == 0) { - // don't check for collection count, a straight clear doesnt initialize the collection - $collection->clear(); - return $collection; - } - - $callback = $this->getOption('explode_callback'); - $values = call_user_func($callback, $this->getOption('separator'), $value); - - if ($this->getOption('trim') === true) { - $values = array_map('trim', $values); - } - - /* @var $em Doctrine\ORM\EntityManager */ - $em = $this->getOption('em'); - $className = $this->getOption('class_name'); - $reflField = $em->getClassMetadata($className) - ->getReflectionProperty($this->getOption('field_name')); - - // 1. removing elements that are not yet present anymore - foreach ($collection as $object) { - $uniqueIdent = $reflField->getValue($object); - $key = array_search($uniqueIdent, $values); - if (false === $key) { - $collection->removeElement($object); - } else { - // found in the collection, no need to do anything with it so remove it - unset($values[$key]); - } - } - - // 2. add elements that are known to the EntityManager but newly connected, query them from the repository - if (count($values)) { - $dql = sprintf('SELECT o FROM %s o WHERE o.%s IN (', $className, $this->getOption('field_name')); - $query = $em->createQuery(); - $needles = array(); - $i = 0; - foreach ($values as $val) { - $query->setParameter(++$i, $val); - $needles[] = '?'.$i; - } - $dql .= implode(',', $needles).')'; - $query->setDql($dql); - $newElements = $query->getResult(); - - foreach ($newElements as $object) { - $collection->add($object); - - $uniqueIdent = $reflField->getValue($object); - $key = array_search($uniqueIdent, $values); - unset($values[$key]); - } - } - - // 3. new elements that are not in the repository have to be created and persisted then attached: - if (count($values)) { - $callback = $this->getOption('create_instance_callback'); - if (!$callback || !is_callable($callback)) { - throw new TransformationFailedException('Cannot transform list of identifiers, because a new element was detected and it is unknown how to create an instance of this element.'); - } - - foreach ($values as $newValue) { - $newInstance = call_user_func($callback, $newValue); - if (!($newInstance instanceof $className)) { - throw new TransformationFailedException(sprintf('Error while trying to create a new instance for the identifier "%s". No new instance was created.', $newValue)); - } - $collection->add($newInstance); - $em->persist($newInstance); - } - } - - return $collection; - } - - /** - * Transform a Doctrine Collection into a string of identifies with a separator. - * - * @param Collection $value - * @return string - */ - public function transform($value) - { - if (null === $value) { - return ''; - } - - $values = array(); - $em = $this->getOption('em'); - $reflField = $em->getClassMetadata($this->getOption('class_name')) - ->getReflectionProperty($this->getOption('field_name')); - - foreach ($value as $object) { - $values[] = $reflField->getValue($object); - } - $callback = $this->getOption('implode_callback'); - - return call_user_func($callback, $this->getOption('separator'), $values); - } -} diff --git a/src/Symfony/Component/Form/ValueTransformer/DateTimeToArrayTransformer.php b/src/Symfony/Component/Form/ValueTransformer/DateTimeToArrayTransformer.php index 1684a10b22a69..e66085c8ee6de 100644 --- a/src/Symfony/Component/Form/ValueTransformer/DateTimeToArrayTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/DateTimeToArrayTransformer.php @@ -95,7 +95,7 @@ public function transform($dateTime) * @param array $value Localized date string/array * @return DateTime Normalized date */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { if (null === $value) { return null; diff --git a/src/Symfony/Component/Form/ValueTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/DateTimeToLocalizedStringTransformer.php index fd2bb2756e69d..e5cc1ec2268e1 100644 --- a/src/Symfony/Component/Form/ValueTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/DateTimeToLocalizedStringTransformer.php @@ -86,7 +86,7 @@ public function transform($dateTime) * @param string|array $value Localized date string/array * @return DateTime Normalized date */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { $inputTimezone = $this->getOption('input_timezone'); diff --git a/src/Symfony/Component/Form/ValueTransformer/DateTimeToStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/DateTimeToStringTransformer.php index 014b39c4b31f5..17b86d7be468a 100644 --- a/src/Symfony/Component/Form/ValueTransformer/DateTimeToStringTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/DateTimeToStringTransformer.php @@ -62,7 +62,7 @@ public function transform($value) * @param string $value A value as produced by PHP's date() function * @return DateTime A DateTime object */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { if (empty($value)) { return null; diff --git a/src/Symfony/Component/Form/ValueTransformer/DateTimeToTimestampTransformer.php b/src/Symfony/Component/Form/ValueTransformer/DateTimeToTimestampTransformer.php index 3be5fc6c115e3..c05575f0da0b7 100644 --- a/src/Symfony/Component/Form/ValueTransformer/DateTimeToTimestampTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/DateTimeToTimestampTransformer.php @@ -60,7 +60,7 @@ public function transform($value) * @param string $value A value as produced by PHP's date() function * @return DateTime A DateTime object */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { if (null === $value) { return null; diff --git a/src/Symfony/Component/Form/ValueTransformer/MoneyToLocalizedStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/MoneyToLocalizedStringTransformer.php index 15acd97207cb3..8db5b4cad9b31 100644 --- a/src/Symfony/Component/Form/ValueTransformer/MoneyToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/MoneyToLocalizedStringTransformer.php @@ -58,9 +58,9 @@ public function transform($value) * @param string $value Localized money string * @return number Normalized number */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { - $value = parent::reverseTransform($value, $originalValue); + $value = parent::reverseTransform($value); if (null !== $value) { $value *= $this->getOption('divisor'); diff --git a/src/Symfony/Component/Form/ValueTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/NumberToLocalizedStringTransformer.php index a960dbdf62c8f..19d9e3012d5f6 100644 --- a/src/Symfony/Component/Form/ValueTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/NumberToLocalizedStringTransformer.php @@ -74,7 +74,7 @@ public function transform($value) * * @param string $value */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { if (!is_string($value)) { throw new UnexpectedTypeException($value, 'string'); diff --git a/src/Symfony/Component/Form/ValueTransformer/PercentToLocalizedStringTransformer.php b/src/Symfony/Component/Form/ValueTransformer/PercentToLocalizedStringTransformer.php index a3762ee788ffb..ece2e4da90476 100644 --- a/src/Symfony/Component/Form/ValueTransformer/PercentToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/PercentToLocalizedStringTransformer.php @@ -82,7 +82,7 @@ public function transform($value) * @param number $value Percentage value. * @return number Normalized value. */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { if (!is_string($value)) { throw new UnexpectedTypeException($value, 'string'); diff --git a/src/Symfony/Component/Form/ValueTransformer/ReversedTransformer.php b/src/Symfony/Component/Form/ValueTransformer/ReversedTransformer.php index 93c068b81839f..99af8c16da5b6 100644 --- a/src/Symfony/Component/Form/ValueTransformer/ReversedTransformer.php +++ b/src/Symfony/Component/Form/ValueTransformer/ReversedTransformer.php @@ -48,7 +48,7 @@ public function transform($value) /** * {@inheritDoc} */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { return $this->reversedTransformer->transform($value); } diff --git a/src/Symfony/Component/Form/ValueTransformer/ValueTransformerChain.php b/src/Symfony/Component/Form/ValueTransformer/ValueTransformerChain.php index a8bbda4dbfba3..129df2dc34c7d 100644 --- a/src/Symfony/Component/Form/ValueTransformer/ValueTransformerChain.php +++ b/src/Symfony/Component/Form/ValueTransformer/ValueTransformerChain.php @@ -66,10 +66,10 @@ public function transform($value) * @param mixed $value The transformed value * @return mixed The reverse-transformed value */ - public function reverseTransform($value, $originalValue) + public function reverseTransform($value) { for ($i = count($this->transformers) - 1; $i >= 0; --$i) { - $value = $this->transformers[$i]->reverseTransform($value, $originalValue); + $value = $this->transformers[$i]->reverseTransform($value); } return $value; diff --git a/src/Symfony/Component/Form/ValueTransformer/ValueTransformerInterface.php b/src/Symfony/Component/Form/ValueTransformer/ValueTransformerInterface.php index 663011d5b8ecd..b170b7412a2ea 100644 --- a/src/Symfony/Component/Form/ValueTransformer/ValueTransformerInterface.php +++ b/src/Symfony/Component/Form/ValueTransformer/ValueTransformerInterface.php @@ -65,11 +65,9 @@ function transform($value); * is passed. * * @param mixed $value The value in the transformed representation - * @param mixed $originalValue The original value from the datasource that is about to be overwritten by the new value. - * @return mixed The value in the original representation * @throws UnexpectedTypeException when the argument is not of the * expected type * @throws ValueTransformerException when the transformation fails */ - function reverseTransform($value, $originalValue); + function reverseTransform($value); } \ No newline at end of file diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php index 8fd995d67351f..50464ea2a3759 100644 --- a/src/Symfony/Component/HttpFoundation/File/File.php +++ b/src/Symfony/Component/HttpFoundation/File/File.php @@ -605,7 +605,7 @@ public function getMimeType() */ public function size() { - if (false === ($size = filesize($this->getPath()))) { + if (false === ($size = @filesize($this->getPath()))) { throw new FileException(sprintf('Could not read file size of %s', $this->getPath())); } @@ -623,7 +623,7 @@ protected function doMove($directory, $filename) { $newPath = $directory . DIRECTORY_SEPARATOR . $filename; - if (!rename($this->getPath(), $newPath)) { + if (!@rename($this->getPath(), $newPath)) { throw new FileException(sprintf('Could not move file %s to %s', $this->getPath(), $newPath)); } diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php index 7ea2d59cdfa7d..0aee4cd978cee 100644 --- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php +++ b/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php @@ -50,17 +50,10 @@ public function guess($path) return null; } - if (!$finfo = new \finfo(FILEINFO_MIME)) { + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE)) { return null; } - $type = $finfo->file($path); - - // remove charset (added as of PHP 5.3) - if (false !== $pos = strpos($type, ';')) { - $type = substr($type, 0, $pos); - } - - return $type; + return $finfo->file($path); } } \ No newline at end of file diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 18a3a1c11583f..57883272c457b 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -223,14 +223,34 @@ static public function create($uri, $method = 'GET', $parameters = array(), $coo public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) { $dup = clone $this; - $dup->initialize( - null !== $query ? $query : $this->query->all(), - null !== $request ? $request : $this->request->all(), - null !== $attributes ? $attributes : $this->attributes->all(), - null !== $cookies ? $cookies : $this->cookies->all(), - null !== $files ? $files : $this->files->all(), - null !== $server ? $server : $this->server->all() - ); + if ($query !== null) { + $dup->query = new ParameterBag($query); + } + if ($request !== null) { + $dup->request = new ParameterBag($request); + } + if ($attributes !== null) { + $dup->attributes = new ParameterBag($attributes); + } + if ($cookies !== null) { + $dup->cookies = new ParameterBag($cookies); + } + if ($files !== null) { + $dup->files = new FileBag($files); + } + if ($server !== null) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $this->languages = null; + $this->charsets = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; return $dup; } diff --git a/src/Symfony/Component/HttpKernel/Bundle/Bundle.php b/src/Symfony/Component/HttpKernel/Bundle/Bundle.php index e214c00cd7238..17b5f75a73c71 100644 --- a/src/Symfony/Component/HttpKernel/Bundle/Bundle.php +++ b/src/Symfony/Component/HttpKernel/Bundle/Bundle.php @@ -25,6 +25,7 @@ abstract class Bundle extends ContainerAware implements BundleInterface { protected $name; + protected $reflected; /** * Boots the Bundle. @@ -40,6 +41,34 @@ public function shutdown() { } + /** + * Gets the Bundle namespace. + * + * @return string The Bundle namespace + */ + public function getNamespace() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + + return $this->reflected->getNamespaceName(); + } + + /** + * Gets the Bundle directory path. + * + * @return string The Bundle absolute path + */ + public function getPath() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + + return strtr(dirname($this->reflected->getFileName()), '\\', '/'); + } + /** * Returns the bundle parent name. * @@ -67,18 +96,6 @@ final public function getName() return $this->name = false === $pos ? $name : substr($name, $pos + 1); } - /** - * Gets the Bundle directory path. - * - * The path should always be returned as a Unix path (with /). - * - * @return string The Bundle absolute path - */ - final public function getNormalizedPath() - { - return strtr($this->getPath(), '\\', '/'); - } - /** * Finds and registers Dependency Injection Container extensions. * @@ -91,7 +108,7 @@ final public function getNormalizedPath() */ public function registerExtensions(ContainerBuilder $container) { - if (!$dir = realpath($this->getNormalizedPath().'/DependencyInjection')) { + if (!$dir = realpath($this->getPath().'/DependencyInjection')) { return; } @@ -118,7 +135,7 @@ public function registerExtensions(ContainerBuilder $container) */ public function registerCommands(Application $application) { - if (!$dir = realpath($this->getNormalizedPath().'/Command')) { + if (!$dir = realpath($this->getPath().'/Command')) { return; } diff --git a/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php b/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php index fd55044e5ef5e..ba7f0ab6b647f 100644 --- a/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php +++ b/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php @@ -49,14 +49,6 @@ function getName(); */ function getNamespace(); - - /** - * Gets the Bundle directory path. - * - * @return string The Bundle absolute path - */ - function getPath(); - /** * Gets the Bundle directory path. * @@ -64,5 +56,5 @@ function getPath(); * * @return string The Bundle absolute path */ - function getNormalizedPath(); + function getPath(); } diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php index fc7b1de3238e0..0f44beb246abf 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php @@ -49,6 +49,8 @@ function getController(Request $request); * @param Request $request A Request instance * @param mixed $controller A PHP callable * + * @return array An array of arguments to pass to the controller + * * @throws \RuntimeException When value for argument given is not provided */ function getArguments(Request $request, $controller); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 0af971fbe8b0a..dfe47a59cbd89 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -39,6 +39,11 @@ public function collect(Request $request, Response $response, \Exception $except $responseHeaders['Set-Cookie'] = $cookies; } + $attributes = array(); + foreach ($request->attributes->all() as $key => $value) { + $attributes[$key] = is_object($value) ? sprintf('Object(%s)', get_class($value)) : $value; + } + $this->data = array( 'format' => $request->getRequestFormat(), 'content_type' => $response->headers->get('Content-Type') ? $response->headers->get('Content-Type') : 'text/html', @@ -48,7 +53,7 @@ public function collect(Request $request, Response $response, \Exception $except 'request_headers' => $request->headers->all(), 'request_server' => $request->server->all(), 'request_cookies' => $request->cookies->all(), - 'request_attributes' => $request->attributes->all(), + 'request_attributes' => $attributes, 'response_headers' => $responseHeaders, 'session_attributes' => $request->hasSession() ? $request->getSession()->getAttributes() : array(), ); diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index f4359f0285ea9..cadbfa9f83263 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -31,6 +31,7 @@ class HttpCache implements HttpKernelInterface protected $store; protected $request; protected $esi; + protected $esiTtls; /** * Constructor. @@ -136,6 +137,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ if (HttpKernelInterface::MASTER_REQUEST === $type) { $this->traces = array(); $this->request = $request; + $this->esiTtls = array(); } $path = $request->getPathInfo(); @@ -160,9 +162,44 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ $response->headers->set('X-Symfony-Cache', $this->getLog()); } + if (null !== $this->esi) { + $this->addEsiTtl($response); + + if ($request === $this->request) { + $this->updateResponseCacheControl($response); + } + } + return $response; } + /** + * Stores the response's TTL locally. + * + * @param Response $response + */ + protected function addEsiTtl(Response $response) + { + $this->esiTtls[] = $response->isValidateable() ? -1 : $response->getTtl(); + } + + /** + * Changes the master response TTL to the smallest TTL received or force validation if + * one of the ESI has validation cache strategy. + * + * @param Response $response + */ + protected function updateResponseCacheControl(Response $response) + { + $ttl = min($this->esiTtls); + if (-1 === $ttl) { + $response->headers->set('Cache-Control', 'no-cache, must-revalidate'); + } else { + $response->setSharedMaxAge($ttl); + $response->setMaxAge(0); + } + } + /** * Forwards the Request to the backend without storing the Response in the cache. * diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index c46d9bf1a150c..86b7ebbe9dc24 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -97,7 +97,7 @@ public function boot() // init container $this->initializeContainer(); - foreach ($this->bundles as $bundle) { + foreach ($this->getBundles() as $bundle) { $bundle->setContainer($this->container); $bundle->boot(); } @@ -114,7 +114,7 @@ public function shutdown() { $this->booted = false; - foreach ($this->bundles as $bundle) { + foreach ($this->getBundles() as $bundle) { $bundle->shutdown(); $bundle->setContainer(null); } @@ -131,7 +131,17 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ $this->boot(); } - return $this->container->get('http_kernel')->handle($request, $type, $catch); + return $this->getHttpKernel()->handle($request, $type, $catch); + } + + /** + * Gets a http kernel from the container + * + * @return HttpKernel + */ + protected function getHttpKernel() + { + return $this->container->get('http_kernel'); } /** @@ -153,7 +163,7 @@ public function getBundles() */ public function isClassInActiveBundle($class) { - foreach ($this->bundles as $bundle) { + foreach ($this->getBundles() as $bundle) { $bundleClass = get_class($bundle); if (0 === strpos($class, substr($bundleClass, 0, strrpos($bundleClass, '\\')))) { return true; @@ -237,7 +247,7 @@ public function locateResource($name, $dir = null, $first = true) } foreach ($this->getBundle($bundle, false) as $bundle) { - if (file_exists($file = $bundle->getNormalizedPath().'/'.$path)) { + if (file_exists($file = $bundle->getPath().'/'.$path)) { if ($first) { return $file; } @@ -343,7 +353,7 @@ protected function initializeBundles() $this->bundles = array(); $topMostBundles = array(); $directChildren = array(); - + foreach ($this->registerBundles() as $bundle) { $name = $bundle->getName(); if (isset($this->bundles[$name])) { @@ -358,11 +368,11 @@ protected function initializeBundles() $directChildren[$parentName] = $name; } else { $topMostBundles[$name] = $bundle; - } + } } // look for orphans - if (count($diff = array_diff(array_keys($directChildren), array_keys($this->bundles)))) { + if (count($diff = array_values(array_diff(array_keys($directChildren), array_keys($this->bundles))))) { throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); } @@ -377,7 +387,7 @@ protected function initializeBundles() array_unshift($bundleMap, $this->bundles[$name]); $hierarchy[] = $name; } - + foreach ($hierarchy as $bundle) { $this->bundleMap[$bundle] = $bundleMap; array_pop($bundleMap); diff --git a/src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php index 25d4cc1f5f77c..0840f8649d22d 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/SQLiteProfilerStorage.php @@ -94,11 +94,11 @@ public function write($token, $data, $ip, $url, $time) ); try { $this->exec($db, 'INSERT INTO data (token, data, ip, url, time, created_at) VALUES (:token, :data, :ip, :url, :time, :created_at)', $args); + $this->cleanup(); $status = true; } catch (\Exception $e) { $status = false; } - $this->cleanup(); $this->close($db); return $status; @@ -146,6 +146,11 @@ protected function initDb() protected function exec($db, $query, array $args = array()) { $stmt = $db->prepare($query); + + if (false === $stmt) { + throw new \RuntimeException('The database cannot successfully prepare the statement'); + } + if ($db instanceof \SQLite3) { foreach ($args as $arg => $val) { $stmt->bindValue($arg, $val, is_int($val) ? \SQLITE3_INTEGER : \SQLITE3_TEXT); @@ -157,7 +162,10 @@ protected function exec($db, $query, array $args = array()) foreach ($args as $arg => $val) { $stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR); } - $stmt->execute(); + $success = $stmt->execute(); + if (!$success) { + throw new \RuntimeException(sprintf('Error executing SQLite query "%s"', $query)); + } } } diff --git a/src/Symfony/Component/HttpKernel/bootstrap.php b/src/Symfony/Component/HttpKernel/bootstrap.php index acdb86346579d..75e70a9a93926 100644 --- a/src/Symfony/Component/HttpKernel/bootstrap.php +++ b/src/Symfony/Component/HttpKernel/bootstrap.php @@ -113,7 +113,7 @@ public function getServiceIds() $ids = array(); $r = new \ReflectionClass($this); foreach ($r->getMethods() as $method) { - if (preg_match('/^get(.+)Service$/', $name = $method->getName(), $match)) { + if (preg_match('/^get(.+)Service$/', $method->getName(), $match)) { $ids[] = self::underscore($match[1]); } } @@ -229,7 +229,6 @@ function getParent(); function getName(); function getNamespace(); function getPath(); - function getNormalizedPath(); } } namespace Symfony\Component\HttpKernel\Bundle @@ -241,12 +240,27 @@ function getNormalizedPath(); abstract class Bundle extends ContainerAware implements BundleInterface { protected $name; + protected $reflected; public function boot() { } public function shutdown() { } + public function getNamespace() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + return $this->reflected->getNamespaceName(); + } + public function getPath() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + return strtr(dirname($this->reflected->getFileName()), '\\', '/'); + } public function getParent() { return null; @@ -260,13 +274,9 @@ final public function getName() $pos = strrpos($name, '\\'); return $this->name = false === $pos ? $name : substr($name, $pos + 1); } - final public function getNormalizedPath() - { - return strtr($this->getPath(), '\\', '/'); - } public function registerExtensions(ContainerBuilder $container) { - if (!$dir = realpath($this->getNormalizedPath().'/DependencyInjection')) { + if (!$dir = realpath($this->getPath().'/DependencyInjection')) { return; } $finder = new Finder(); @@ -279,7 +289,7 @@ public function registerExtensions(ContainerBuilder $container) } public function registerCommands(Application $application) { - if (!$dir = realpath($this->getNormalizedPath().'/Command')) { + if (!$dir = realpath($this->getPath().'/Command')) { return; } $finder = new Finder(); @@ -577,7 +587,7 @@ public function locateResource($name, $dir = null, $first = true) $files[] = $file; } foreach ($this->getBundle($bundle, false) as $bundle) { - if (file_exists($file = $bundle->getNormalizedPath().'/'.$path)) { + if (file_exists($file = $bundle->getPath().'/'.$path)) { if ($first) { return $file; } @@ -1233,14 +1243,34 @@ static public function create($uri, $method = 'GET', $parameters = array(), $coo public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) { $dup = clone $this; - $dup->initialize( - null !== $query ? $query : $this->query->all(), - null !== $request ? $request : $this->request->all(), - null !== $attributes ? $attributes : $this->attributes->all(), - null !== $cookies ? $cookies : $this->cookies->all(), - null !== $files ? $files : $this->files->all(), - null !== $server ? $server : $this->server->all() - ); + if ($query !== null) { + $dup->query = new ParameterBag($query); + } + if ($request !== null) { + $dup->request = new ParameterBag($request); + } + if ($attributes !== null) { + $dup->attributes = new ParameterBag($attributes); + } + if ($cookies !== null) { + $dup->cookies = new ParameterBag($cookies); + } + if ($files !== null) { + $dup->files = new FileBag($files); + } + if ($server !== null) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $this->languages = null; + $this->charsets = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; return $dup; } public function __clone() diff --git a/src/Symfony/Component/HttpKernel/bootstrap_cache.php b/src/Symfony/Component/HttpKernel/bootstrap_cache.php index 051f3bc25b43c..e1bbfc66aa100 100644 --- a/src/Symfony/Component/HttpKernel/bootstrap_cache.php +++ b/src/Symfony/Component/HttpKernel/bootstrap_cache.php @@ -151,7 +151,7 @@ public function locateResource($name, $dir = null, $first = true) $files[] = $file; } foreach ($this->getBundle($bundle, false) as $bundle) { - if (file_exists($file = $bundle->getNormalizedPath().'/'.$path)) { + if (file_exists($file = $bundle->getPath().'/'.$path)) { if ($first) { return $file; } diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php index cc90ee8dc1ffc..1a39019abd799 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -35,8 +35,9 @@ class Router implements RouterInterface * * Available options: * - * * cache_dir: The cache directory (or null to disable caching) - * * debug: Whether to enable debugging or not (false by default) + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * * resource_type: Type hint for the main resource (optional) * * @param LoaderInterface $loader A LoaderInterface instance * @param mixed $resource The main resource to load @@ -63,6 +64,7 @@ public function __construct(LoaderInterface $loader, $resource, array $options = 'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper', 'matcher_cache_class' => 'ProjectUrlMatcher', + 'resource_type' => null, ); // check option names @@ -81,7 +83,7 @@ public function __construct(LoaderInterface $loader, $resource, array $options = public function getRouteCollection() { if (null === $this->collection) { - $this->collection = $this->loader->load($this->resource); + $this->collection = $this->loader->load($this->resource, $this->options['resource_type']); } return $this->collection; diff --git a/src/Symfony/Component/Templating/DelegatingEngine.php b/src/Symfony/Component/Templating/DelegatingEngine.php index 9c558961dea7f..8717ef0f3d620 100644 --- a/src/Symfony/Component/Templating/DelegatingEngine.php +++ b/src/Symfony/Component/Templating/DelegatingEngine.php @@ -103,6 +103,15 @@ public function supports($name) return false; } + /** + * Get an engine able to render the given template. + * + * @param string $name A template name + * + * @return EngineInterface The engine + * + * @throws \RuntimeException if no engine able to work with the template is found + */ protected function getEngine($name) { foreach ($this->engines as $engine) { diff --git a/src/Symfony/Component/Templating/PhpEngine.php b/src/Symfony/Component/Templating/PhpEngine.php index c38898383d671..4fbaf13bb06c4 100644 --- a/src/Symfony/Component/Templating/PhpEngine.php +++ b/src/Symfony/Component/Templating/PhpEngine.php @@ -221,6 +221,11 @@ public function addHelpers(array $helpers = array()) } } + /** + * Sets the helpers. + * + * @params Helper[] $helpers An array of helper + */ public function setHelpers(array $helpers) { $this->helpers = array(); @@ -286,7 +291,8 @@ public function extend($template) /** * Escapes a string by using the current charset. * - * @param mixed $value A variable to escape + * @param mixed $value A variable to escape + * @param string $context The context name * * @return string The escaped value */ @@ -440,6 +446,17 @@ function ($value) use ($that) ); } + /** + * Convert a string from one encoding to another. + * + * @param string $string The string to convert + * @param string $to The input encoding + * @param string $from The output encoding + * + * @return string The string with the new encoding + * + * @throws \RuntimeException if no suitable encoding function is found (iconv or mbstring) + */ public function convertEncoding($string, $to, $from) { if (function_exists('iconv')) { diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index bb7f3057fc7e5..9cc7b5932b21a 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -379,6 +379,11 @@ static protected function evaluateScalar($scalar) } } + /** + * Get a regex that match a unix timestamp + * + * @return string The regular expression + */ static protected function getTimestampRegex() { return <<assertEquals('foo', $client->getResponse()->getContent(), '->getCrawler() returns the Response of the last request'); } - /** - * @covers Symfony\Component\BrowserKit\Client::getContent - */ public function testGetContent() { $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Compiler/CheckDefinitionValidityPassTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/CheckDefinitionValidityPassTest.php new file mode 100644 index 0000000000000..73d46bbc3853f --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Compiler/CheckDefinitionValidityPassTest.php @@ -0,0 +1,61 @@ +register('a')->setSynthetic(true)->setPublic(false); + + $this->process($container); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsSyntheticPrototypeDefinitions() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(true)->setScope(ContainerInterface::SCOPE_PROTOTYPE); + + $this->process($container); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsNonSyntheticNonAbstractDefinitionWithoutClass() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(false)->setAbstract(false); + + $this->process($container); + } + + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a', 'class'); + $container->register('b', 'class')->setSynthetic(true)->setPublic(true); + $container->register('c', 'class')->setAbstract(true); + $container->register('d', 'class')->setSynthetic(true); + + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $pass = new CheckDefinitionValidityPass(); + $pass->process($container); + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Configuration/ArrayNodeTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/ArrayNodeTest.php new file mode 100644 index 0000000000000..da3155493e241 --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/ArrayNodeTest.php @@ -0,0 +1,17 @@ +normalize(false); + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Configuration/FinalizationTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/FinalizationTest.php new file mode 100644 index 0000000000000..ab3f5af12babe --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/FinalizationTest.php @@ -0,0 +1,59 @@ +root('config', 'array') + ->node('level1', 'array') + ->canBeUnset() + ->node('level2', 'array') + ->canBeUnset() + ->node('somevalue', 'scalar')->end() + ->node('anothervalue', 'scalar')->end() + ->end() + ->node('level1_scalar', 'scalar')->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'level1' => array( + 'level2' => array( + 'somevalue' => 'foo', + 'anothervalue' => 'bar', + ), + 'level1_scalar' => 'foo', + ), + ); + + $b = array( + 'level1' => array( + 'level2' => false, + ), + ); + + $this->assertEquals(array( + 'level1' => array( + 'level1_scalar' => 'foo', + ), + ), $this->process($tree, array($a, $b))); + } + + protected function process(NodeInterface $tree, array $configs) + { + $processor = new Processor(); + + return $processor->process($tree, $configs); + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Configuration/MergeTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/MergeTest.php new file mode 100644 index 0000000000000..64fc6da4eda72 --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/MergeTest.php @@ -0,0 +1,170 @@ +root('root', 'array') + ->node('foo', 'scalar') + ->cannotBeOverwritten() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'foo' => 'bar', + ); + + $b = array( + 'foo' => 'moo', + ); + + $tree->merge($a, $b); + } + + public function testUnsetKey() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->node('unsettable', 'array') + ->canBeUnset() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->node('unsetted', 'array') + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'foo' => 'bar', + 'unsettable' => array( + 'foo' => 'a', + 'bar' => 'b', + ), + 'unsetted' => false, + ); + + $b = array( + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => array('a', 'b'), + ); + + $this->assertEquals(array( + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => array('a', 'b'), + ), $tree->merge($a, $b)); + } + + /** + * @expectedException Symfony\Component\DependencyInjection\Configuration\Exception\InvalidConfigurationException + */ + public function testDoesNotAllowNewKeysInSubsequentConfigs() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->node('test', 'array') + ->disallowNewKeysInSubsequentConfigs() + ->useAttributeAsKey('key') + ->prototype('array') + ->node('value', 'scalar')->end() + ->end() + ->end() + ->end() + ->buildTree(); + + $a = array( + 'test' => array( + 'a' => array('value' => 'foo') + ) + ); + + $b = array( + 'test' => array( + 'b' => array('value' => 'foo') + ) + ); + + $tree->merge($a, $b); + } + + public function testPerformsNoDeepMerging() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->node('no_deep_merging', 'array') + ->performNoDeepMerging() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'no_deep_merging' => array( + 'foo' => 'a', + 'bar' => 'b', + ), + ); + + $b = array( + 'no_deep_merging' => array( + 'c' => 'd', + ) + ); + + $this->assertEquals(array( + 'no_deep_merging' => array( + 'c' => 'd', + ) + ), $tree->merge($a, $b)); + } + + public function testPrototypeWithoutAKeyAttribute() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->node('append_elements', 'array') + ->prototype('scalar')->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'append_elements' => array('a', 'b'), + ); + + $b = array( + 'append_elements' => array('c', 'd'), + ); + + $this->assertEquals(array('append_elements' => array('a', 'b', 'c', 'd')), $tree->merge($a, $b)); + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Configuration/NormalizationTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/NormalizationTest.php index 4afbdb9eeeb86..9254700eb63ff 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/Configuration/NormalizationTest.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/NormalizationTest.php @@ -15,11 +15,11 @@ public function testNormalizeEncoders($denormalized) $tb = new TreeBuilder(); $tree = $tb ->root('root_name', 'array') - ->normalize('encoder') + ->fixXmlConfig('encoder') ->node('encoders', 'array') - ->key('class') + ->useAttributeAsKey('class') ->prototype('array') - ->before()->ifString()->then(function($v) { return array('algorithm' => $v); })->end() + ->beforeNormalization()->ifString()->then(function($v) { return array('algorithm' => $v); })->end() ->node('algorithm', 'scalar')->end() ->end() ->end() @@ -87,7 +87,7 @@ public function testAnonymousKeysArray($denormalized) $tree = $tb ->root('root', 'array') ->node('logout', 'array') - ->normalize('handler') + ->fixXmlConfig('handler') ->node('handlers', 'array') ->prototype('scalar')->end() ->end() diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Configuration/ScalarNodeTest.php b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/ScalarNodeTest.php new file mode 100644 index 0000000000000..8ff43ca073b46 --- /dev/null +++ b/tests/Symfony/Tests/Component/DependencyInjection/Configuration/ScalarNodeTest.php @@ -0,0 +1,49 @@ +assertSame($value, $node->normalize($value)); + } + + public function getValidValues() + { + return array( + array(false), + array(true), + array(null), + array(''), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException Symfony\Component\DependencyInjection\Configuration\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new ScalarNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/DependencyInjection/ContainerBuilderTest.php b/tests/Symfony/Tests/Component/DependencyInjection/ContainerBuilderTest.php index b9d68f072e889..4a14e40664364 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/ContainerBuilderTest.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/ContainerBuilderTest.php @@ -247,7 +247,7 @@ public function testCreateServiceFactoryMethod() { $builder = new ContainerBuilder(); $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'FooClass')->setFactoryMethod('getInstance')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'))); + $builder->register('foo1', 'FooClass')->setFactoryClass('FooClass')->setFactoryMethod('getInstance')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'))); $builder->setParameter('value', 'bar'); $this->assertTrue($builder->get('foo1')->called, '->createService() calls the factory method to create the service instance'); $this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', $builder->get('bar')), $builder->get('foo1')->arguments, '->createService() passes the arguments to the factory method'); diff --git a/tests/Symfony/Tests/Component/DependencyInjection/DefinitionDecoratorTest.php b/tests/Symfony/Tests/Component/DependencyInjection/DefinitionDecoratorTest.php index ff80fba5d83b8..6c74adfe4a42d 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/DefinitionDecoratorTest.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/DefinitionDecoratorTest.php @@ -34,8 +34,9 @@ public function getPropertyTests() { return array( array('class', 'class'), - array('factoryService', 'factory_service'), + array('factoryClass', 'factory_class'), array('factoryMethod', 'factory_method'), + array('factoryService', 'factory_service'), array('configurator', 'configurator'), array('file', 'file'), ); diff --git a/tests/Symfony/Tests/Component/DependencyInjection/DefinitionTest.php b/tests/Symfony/Tests/Component/DependencyInjection/DefinitionTest.php index 41ded4f06bc35..5a6097dcd3156 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/DefinitionTest.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/DefinitionTest.php @@ -27,13 +27,18 @@ public function testConstructor() $this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument'); } - /** - * @covers Symfony\Component\DependencyInjection\Definition::setFactoryMethod - * @covers Symfony\Component\DependencyInjection\Definition::getFactoryMethod - */ - public function testSetGetConstructor() + public function testSetGetFactoryClass() + { + $def = new Definition('stdClass'); + $this->assertNull($def->getFactoryClass()); + $this->assertSame($def, $def->setFactoryClass('stdClass2'), "->setFactoryClass() implements a fluent interface."); + $this->assertEquals('stdClass2', $def->getFactoryClass(), "->getFactoryClass() returns current class to construct this service."); + } + + public function testSetGetFactoryMethod() { $def = new Definition('stdClass'); + $this->assertNull($def->getFactoryMethod()); $this->assertSame($def, $def->setFactoryMethod('foo'), '->setFactoryMethod() implements a fluent interface'); $this->assertEquals('foo', $def->getFactoryMethod(), '->getFactoryMethod() returns the factory method name'); } @@ -42,8 +47,8 @@ public function testSetGetFactoryService() { $def = new Definition('stdClass'); $this->assertNull($def->getFactoryService()); - $this->assertSame($def, $def->setFactoryService('stdClass2'), "->setFactoryService() implements a fluent interface."); - $this->assertEquals('stdClass2', $def->getFactoryService(), "->getFactoryService() returns current service to construct this service."); + $this->assertSame($def, $def->setFactoryService('foo.bar'), "->setFactoryService() implements a fluent interface."); + $this->assertEquals('foo.bar', $def->getFactoryService(), "->getFactoryService() returns current service to construct this service."); } /** diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/container9.php b/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/container9.php index 46c9dbb90932f..03e4c9db92829 100644 --- a/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/container9.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/container9.php @@ -12,6 +12,7 @@ register('foo', 'FooClass')-> addTag('foo', array('foo' => 'foo'))-> addTag('foo', array('bar' => 'bar'))-> + setFactoryClass('FooClass')-> setFactoryMethod('getInstance')-> setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, new Reference('service_container')))-> setScope('prototype')-> @@ -27,6 +28,7 @@ ; $container-> register('foo.baz', '%baz_class%')-> + setFactoryClass('%baz_class%')-> setFactoryMethod('getInstance')-> setConfigurator(array('%baz_class%', 'configureStatic1')) ; diff --git a/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces1.php b/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces1.php index c3efeb9f47207..27503a351c674 100755 --- a/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces1.php +++ b/tests/Symfony/Tests/Component/DependencyInjection/Fixtures/containers/interfaces1.php @@ -12,12 +12,14 @@ return $container; -class FooClass -{ - public $bar; - - public function setBar($bar) +if (!class_exists('FooClass')) { + class FooClass { - $this->bar = $bar; + public $bar; + + public function setBar($bar) + { + $this->bar = $bar; + } } -} \ No newline at end of file +} diff --git a/tests/Symfony/Tests/Component/Form/FormTest.php b/tests/Symfony/Tests/Component/Form/FormTest.php index 128fd50f6437d..da609f48b27ba 100644 --- a/tests/Symfony/Tests/Component/Form/FormTest.php +++ b/tests/Symfony/Tests/Component/Form/FormTest.php @@ -41,6 +41,59 @@ protected function configure() } } +// behaves like a form with a value transformer that transforms into +// a specific format +class FormTest_FormThatReturns extends Form +{ + protected $returnValue; + + public function setReturnValue($returnValue) + { + $this->returnValue = $returnValue; + } + + public function setData($data) + { + } + + public function getData() + { + return $this->returnValue; + } +} + +class FormTest_AuthorWithoutRefSetter +{ + protected $reference; + + protected $referenceCopy; + + public function __construct($reference) + { + $this->reference = $reference; + $this->referenceCopy = $reference; + } + + // The returned object should be modified by reference without having + // to provide a setReference() method + public function getReference() + { + return $this->reference; + } + + // The returned object is a copy, so setReferenceCopy() must be used + // to update it + public function getReferenceCopy() + { + return is_object($this->referenceCopy) ? clone $this->referenceCopy : $this->referenceCopy; + } + + public function setReferenceCopy($reference) + { + $this->referenceCopy = $reference; + } +} + class TestSetDataBeforeConfigureForm extends Form { protected $testCase; @@ -223,6 +276,16 @@ public function testValidationGroupsAreNotInheritedFromParentIfSet() $this->assertEquals(array('group2'), $childForm->getValidationGroups()); } + /** + * @expectedException Symfony\Component\Form\Exception\FormException + */ + public function testBindThrowsExceptionIfAnonymous() + { + $form = new Form(null, array('validator' => $this->createMockValidator())); + + $form->bind($this->createPostRequest()); + } + public function testBindValidatesData() { $form = new Form('author', array( @@ -928,6 +991,37 @@ public function testSetDataMatchesAgainstDataClass_succeeds() $form->setData(new Author()); } + public function testSetDataToNull() + { + $form = new Form('author'); + $form->setData(null); + + $this->assertNull($form->getData()); + } + + public function testSetDataToNullCreatesObjectIfClassAvailable() + { + $form = new Form('author', array( + 'data_class' => 'Symfony\Tests\Component\Form\Fixtures\Author', + )); + $form->setData(null); + + $this->assertEquals(new Author(), $form->getData()); + } + + public function testSetDataToNullUsesDataConstructorOption() + { + $author = new Author(); + $form = new Form('author', array( + 'data_constructor' => function () use ($author) { + return $author; + } + )); + $form->setData(null); + + $this->assertSame($author, $form->getData()); + } + public function testSubmitUpdatesTransformedDataFromAllFields() { $originalAuthor = new Author(); @@ -1130,6 +1224,94 @@ public function testValidateDataDoesNotWalkScalars() $form->validateData($context); } + public function testSubformDoesntCallSetters() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $form = new Form('author', array('validator' => $this->createMockValidator())); + $form->setData($author); + $refForm = new Form('reference'); + $refForm->add(new TestField('firstName')); + $form->add($refForm); + + $form->bind($this->createPostRequest(array( + 'author' => array( + // reference has a getter, but not setter + 'reference' => array( + 'firstName' => 'Foo', + ) + ) + ))); + + $this->assertEquals('Foo', $author->getReference()->firstName); + } + + public function testSubformCallsSettersIfByReferenceIsFalse() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $form = new Form('author', array('validator' => $this->createMockValidator())); + $form->setData($author); + $refForm = new Form('referenceCopy', array('by_reference' => false)); + $refForm->add(new TestField('firstName')); + $form->add($refForm); + + $form->bind($this->createPostRequest(array( + 'author' => array( + // referenceCopy has a getter that returns a copy + 'referenceCopy' => array( + 'firstName' => 'Foo', + ) + ) + ))); + + // firstName can only be updated if setReferenceCopy() was called + $this->assertEquals('Foo', $author->getReferenceCopy()->firstName); + } + + public function testSubformCallsSettersIfReferenceIsScalar() + { + $author = new FormTest_AuthorWithoutRefSetter('scalar'); + + $form = new Form('author', array('validator' => $this->createMockValidator())); + $form->setData($author); + $refForm = new FormTest_FormThatReturns('referenceCopy'); + $refForm->setReturnValue('foobar'); + $form->add($refForm); + + $form->bind($this->createPostRequest(array( + 'author' => array( + 'referenceCopy' => array(), // doesn't matter actually + ) + ))); + + // firstName can only be updated if setReferenceCopy() was called + $this->assertEquals('foobar', $author->getReferenceCopy()); + } + + public function testSubformAlwaysInsertsIntoArrays() + { + $ref1 = new Author(); + $ref2 = new Author(); + $author = array('referenceCopy' => $ref1); + + $form = new Form('author', array('validator' => $this->createMockValidator())); + $form->setData($author); + $refForm = new FormTest_FormThatReturns('referenceCopy'); + $refForm->setReturnValue($ref2); + $form->add($refForm); + + $form->bind($this->createPostRequest(array( + 'author' => array( + 'referenceCopy' => array(), // doesn't matter actually + ) + ))); + + // the new reference was inserted into the array + $author = $form->getData(); + $this->assertSame($ref2, $author['referenceCopy']); + } + /** * Create a group containing two fields, "visibleField" and "hiddenField" * diff --git a/tests/Symfony/Tests/Component/Form/ValueTransformer/CollectionToStringTransformerTest.php b/tests/Symfony/Tests/Component/Form/ValueTransformer/CollectionToStringTransformerTest.php deleted file mode 100644 index 97565c4070cec..0000000000000 --- a/tests/Symfony/Tests/Component/Form/ValueTransformer/CollectionToStringTransformerTest.php +++ /dev/null @@ -1,223 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Tests\Component\Form\ValueTransformer; - -require_once __DIR__.'/../DoctrineOrmTestCase.php'; - -use Symfony\Tests\Component\Form\DoctrineOrmTestCase; -use Symfony\Component\Form\ValueTransformer\CollectionToStringTransformer; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\ORM\Tools\SchemaTool; - -class CollectionToStringTransformerTest extends DoctrineOrmTestCase -{ - /** - * @var EntityManager - */ - private $em; - - protected function setUp() - { - parent::setUp(); - $this->em = $this->createTestEntityManager(); - - $schemaTool = new SchemaTool($this->em); - $classes = array($this->em->getClassMetadata(__NAMESPACE__.'\Tag')); - try { - $schemaTool->dropSchema($classes); - } catch(\Exception $e) { - - } - try { - $schemaTool->createSchema($classes); - } catch(\Exception $e) { - echo $e->getMessage(); - } - } - - public function testNoEntityManagerThrowsException() - { - $this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException'); - $transformer = new CollectionToStringTransformer(array( - 'class_name' => __NAMESPACE__.'\Tag', - 'field_name' => 'name', - )); - } - - public function testNoClassNameThrowsException() - { - $this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException'); - $transformer = new CollectionToStringTransformer(array( - 'field_name' => 'name', - 'em' => $this->em, - )); - } - - public function testNoFieldNameThrowsException() - { - $this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException'); - $transformer = new CollectionToStringTransformer(array( - 'class_name' => __NAMESPACE__.'\Tag', - 'em' => $this->em, - )); - } - - public function createTransformer() - { - $transformer = new CollectionToStringTransformer(array( - 'class_name' => __NAMESPACE__.'\Tag', - 'field_name' => 'name', - 'em' => $this->em, - 'create_instance_callback' => function($tagName) { - return new Tag($tagName); - } - )); - return $transformer; - } - - public function testTransformEmptyCollection() - { - $transformer = $this->createTransformer(); - $ret = $transformer->transform(new ArrayCollection()); - - $this->assertEquals("", $ret); - } - - /** - * @depends testTransformEmptyCollection - */ - public function testTransformCollection() - { - $transformer = $this->createTransformer(); - - $tags = new ArrayCollection(); - $tags->add(new Tag("foo")); - $tags->add(new Tag("bar")); - - $this->assertEquals("foo,bar", $transformer->transform($tags)); - } - - public function createTagCollection() - { - $tags = new ArrayCollection(); - $tags->add(new Tag("foo")); - $tags->add(new Tag("bar")); - - return $tags; - } - - /** - * @depends testTransformEmptyCollection - */ - public function testReverseTransformEmptyString() - { - $transformer = $this->createTransformer(); - - $col = new ArrayCollection(); - - $newCol = $transformer->reverseTransform("", $col); - $this->assertSame($col, $newCol, "A collection is an expenive object that is re-used by the transformer!"); - $this->assertEquals(0, count($newCol)); - } - - /** - * @depends testReverseTransformEmptyString - */ - public function testReverseTransformEmptyStringEmptiesCollection() - { - $transformer = $this->createTransformer(); - - $col = $this->createTagCollection(); - - $newCol = $transformer->reverseTransform("", $col); - $this->assertSame($col, $newCol, "A collection is an expenive object that is re-used by the transformer!"); - $this->assertEquals(0, count($newCol)); - } - - /** - * @depends testTransformEmptyCollection - */ - public function testReverseTransformUnchanged() - { - $transformer = $this->createTransformer(); - - $tags = $this->createTagCollection(); - - $tags = $transformer->reverseTransform("foo,bar", $tags); - - $this->assertEquals(2, count($tags)); - } - - /** - * @depends testTransformEmptyCollection - */ - public function testReverseTransformNewKnownEntity() - { - $transformer = $this->createTransformer(); - - $newTag = new Tag("baz"); - $this->em->persist($newTag); - $this->em->flush(); - - $tags = $this->createTagCollection(); - $tags = $transformer->reverseTransform("foo, bar, baz", $tags); - - $this->assertEquals(3, count($tags)); - $this->assertTrue($tags->contains($newTag)); - } - - /** - * @depends testReverseTransformNewKnownEntity - */ - public function testReverseTransformNewUnknownEntity() - { - $transformer = $this->createTransformer(); - - $tags = $this->createTagCollection(); - $tags = $transformer->reverseTransform("foo, bar, baz", $tags); - - $this->assertEquals(3, count($tags)); - $this->em->flush(); - - $this->assertSame($this->em, $transformer->getOption('em')); - - $this->assertEquals(1, count($this->em->getRepository(__NAMESPACE__.'\Tag')->findAll())); - } - - /** - * @depends testReverseTransformNewUnknownEntity - */ - public function testReverseTransformRemoveEntity() - { - $transformer = $this->createTransformer(); - - $tags = $this->createTagCollection(); - $tags = $transformer->reverseTransform("foo", $tags); - - $this->assertEquals(1, count($tags)); - } - -} - -/** @Entity */ -class Tag -{ - /** @Id @GeneratedValue @Column(type="integer") */ - public $id; - - /** @Column(type="string") */ - public $name; - - public function __construct($name) { - $this->name = $name; - } -} \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/HttpFoundation/CookieTest.php b/tests/Symfony/Tests/Component/HttpFoundation/CookieTest.php new file mode 100644 index 0000000000000..7ab76a4c1291c --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/CookieTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\Cookie; + +/** + * CookieTest + * + * @author John Kary + */ +class CookieTest extends \PHPUnit_Framework_TestCase +{ + public function invalidNames() + { + return array( + array(''), + array(",MyName"), + array(";MyName"), + array(" MyName"), + array("\tMyName"), + array("\rMyName"), + array("\nMyName"), + array("\013MyName"), + array("\014MyName"), + ); + } + + public function invalidValues() + { + return array( + array(",MyValue"), + array(";MyValue"), + array(" MyValue"), + array("\tMyValue"), + array("\rMyValue"), + array("\nMyValue"), + array("\013MyValue"), + array("\014MyValue"), + ); + } + + /** + * @dataProvider invalidNames + * @expectedException InvalidArgumentException + * @covers Symfony\Component\HttpFoundation\Cookie::__construct + */ + public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name) + { + new Cookie($name); + } + + /** + * @dataProvider invalidValues + * @expectedException InvalidArgumentException + * @covers Symfony\Component\HttpFoundation\Cookie::__construct + */ + public function testInstantiationThrowsExceptionIfCookieValueContainsInvalidCharacters($value) + { + new Cookie('MyCookie', $value); + } + + /** + * @covers Symfony\Component\HttpFoundation\Cookie::getValue + */ + public function testGetValue() + { + $value = 'MyValue'; + $cookie = new Cookie('MyCookie', $value); + + $this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value'); + } +} diff --git a/tests/Symfony/Tests/Component/HttpFoundation/File/FileTest.php b/tests/Symfony/Tests/Component/HttpFoundation/File/FileTest.php index 15048f7c3dd84..7f5e41a7be800 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/File/FileTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/File/FileTest.php @@ -28,10 +28,16 @@ public function testGetPathReturnsAbsolutePath() $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'test.gif', $this->file->getPath()); } + public function test__toString() + { + $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'test.gif', (string) $this->file); + } + public function testGetWebPathReturnsPathRelativeToDocumentRoot() { File::setDocumentRoot(__DIR__); + $this->assertEquals(__DIR__, File::getDocumentRoot()); $this->assertEquals('/Fixtures/test.gif', $this->file->getWebPath()); } @@ -42,11 +48,24 @@ public function testGetWebPathReturnsEmptyPathIfOutsideDocumentRoot() $this->assertEquals('', $this->file->getWebPath()); } + public function testSetDocumentRootThrowsLogicExceptionWhenNotExists() + { + $this->setExpectedException('LogicException'); + + File::setDocumentRoot(__DIR__.'/Fixtures/not_here'); + } + public function testGetNameReturnsNameWithExtension() { $this->assertEquals('test.gif', $this->file->getName()); } + public function testGetExtensionReturnsEmptyString() + { + $file = new File(__DIR__.'/Fixtures/test'); + $this->assertEquals('', $file->getExtension()); + } + public function testGetExtensionReturnsExtensionWithDot() { $this->assertEquals('.gif', $this->file->getExtension()); @@ -66,6 +85,13 @@ public function testGetMimeTypeUsesMimeTypeGuessers() $this->assertEquals('image/gif', $this->file->getMimeType()); } + public function testGetDefaultExtensionWithoutGuesser() + { + $file = new File(__DIR__.'/Fixtures/directory/.empty'); + + $this->assertEquals('.empty', $file->getDefaultExtension()); + } + public function testGetDefaultExtensionIsBasedOnMimeType() { $file = new File(__DIR__.'/Fixtures/test'); @@ -76,11 +102,33 @@ public function testGetDefaultExtensionIsBasedOnMimeType() $this->assertEquals('.gif', $file->getDefaultExtension()); } + public function testConstructWhenFileNotExists() + { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + + new File(__DIR__.'/Fixtures/not_here'); + } + public function testSizeReturnsFileSize() { $this->assertEquals(filesize($this->file->getPath()), $this->file->size()); } + public function testSizeFailing() + { + $dir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'directory'; + $path = $dir.DIRECTORY_SEPARATOR.'test.copy.gif'; + @unlink($path); + copy(__DIR__.'/Fixtures/test.gif', $path); + + $file = new File($path); + @unlink($path); + + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileException'); + $file->size($path); + + } + public function testMove() { $path = __DIR__.'/Fixtures/test.copy.gif'; @@ -121,6 +169,27 @@ public function testMoveWithNewName() @unlink($targetPath); } + public function testMoveFailing() + { + $path = __DIR__.'/Fixtures/test.copy.gif'; + $targetPath = '/thisfolderwontexist'; + @unlink($path); + @unlink($targetPath); + copy(__DIR__.'/Fixtures/test.gif', $path); + + $file = new File($path); + + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileException'); + $file->move($targetPath); + + $this->assertFileExists($path); + $this->assertFileNotExists($path.$targetPath.'test.gif'); + $this->assertEquals($path, $file->getPath()); + + @unlink($path); + @unlink($targetPath); + } + public function testRename() { $path = __DIR__.'/Fixtures/test.copy.gif'; diff --git a/tests/Symfony/Tests/Component/HttpFoundation/File/Fixtures/.unknownextension b/tests/Symfony/Tests/Component/HttpFoundation/File/Fixtures/.unknownextension new file mode 100644 index 0000000000000..4d1ae35ba2c8e --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/File/Fixtures/.unknownextension @@ -0,0 +1 @@ +f \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/HttpFoundation/File/MimeType/MimeTypeTest.php b/tests/Symfony/Tests/Component/HttpFoundation/File/MimeType/MimeTypeTest.php new file mode 100644 index 0000000000000..957f2c0375e59 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/File/MimeType/MimeTypeTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\ContentTypeMimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +class MimeTypeTest extends \PHPUnit_Framework_TestCase +{ + protected $path; + + public function testGuessImageWithoutExtension() + { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithContentTypeMimeTypeGuesser() + { + $guesser = MimeTypeGuesser::getInstance(); + $guesser->register(new ContentTypeMimeTypeGuesser()); + $this->assertEquals('image/gif', $guesser->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithFileBinaryMimeTypeGuesser() + { + $guesser = MimeTypeGuesser::getInstance(); + $guesser->register(new FileBinaryMimeTypeGuesser()); + $this->assertEquals('image/gif', $guesser->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithKnownExtension() + { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif')); + } + + public function testGuessFileWithUnknownExtension() + { + $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension')); + } + + public function testGuessWithIncorrectPath() + { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here'); + } + + public function testGuessWithNonReadablePath() + { + $path = __DIR__.'/../Fixtures/to_delete'; + touch($path); + chmod($path, 0333); + + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException'); + MimeTypeGuesser::getInstance()->guess($path); + } + + public static function tearDownAfterClass() + { + $path = __DIR__.'/../Fixtures/to_delete'; + if (file_exists($path)) { + chmod($path, 0666); + @unlink($path); + } + } +} diff --git a/tests/Symfony/Tests/Component/HttpFoundation/File/UploadedFileTest.php b/tests/Symfony/Tests/Component/HttpFoundation/File/UploadedFileTest.php index 5e5cb5d69677a..fe2fa432a4302 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/File/UploadedFileTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/File/UploadedFileTest.php @@ -15,6 +15,7 @@ class UploadedFileTest extends \PHPUnit_Framework_TestCase { + public function testFileUploadsMustBeEnabled() { // we can't change this setting without modifying php.ini :( @@ -31,6 +32,42 @@ public function testFileUploadsMustBeEnabled() } } + public function testFileUploadsWithNoMimeType() + { + // we can't change this setting without modifying php.ini :( + if (ini_get('file_uploads')) { + + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK + ); + + $this->assertAttributeEquals('application/octet-stream', 'mimeType', $file); + $this->assertEquals('image/gif', $file->getMimeType()); + } + } + + public function testFileUploadsWithUnknownMimeType() + { + // we can't change this setting without modifying php.ini :( + if (ini_get('file_uploads')) { + + $file = new UploadedFile( + __DIR__.'/Fixtures/.unknownextension', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/.unknownextension'), + UPLOAD_ERR_OK + ); + + $this->assertAttributeEquals('application/octet-stream', 'mimeType', $file); + $this->assertEquals('application/octet-stream', $file->getMimeType()); + } + } + public function testErrorIsOkByDefault() { // we can't change this setting without modifying php.ini :( @@ -46,4 +83,19 @@ public function testErrorIsOkByDefault() $this->assertEquals(UPLOAD_ERR_OK, $file->getError()); } } + public function testGetOriginalName() + { + // we can't change this setting without modifying php.ini :( + if (ini_get('file_uploads')) { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals('original.gif', $file->getOriginalName()); + } + } } \ No newline at end of file diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php index 59be4968a83d1..467e7c8735ea7 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php @@ -54,28 +54,37 @@ public function testCreate() $this->assertEquals('/foo', $request->getPathInfo()); $this->assertEquals('bar=baz', $request->getQueryString()); $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); $request = Request::create('https://test.com/foo?bar=baz'); $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri()); $this->assertEquals('/foo', $request->getPathInfo()); $this->assertEquals('bar=baz', $request->getQueryString()); $this->assertEquals(443, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertTrue($request->isSecure()); $request = Request::create('test.com:90/foo'); $this->assertEquals('http://test.com:90/foo', $request->getUri()); $this->assertEquals('/foo', $request->getPathInfo()); $this->assertEquals('test.com', $request->getHost()); + $this->assertEquals('test.com:90', $request->getHttpHost()); $this->assertEquals(90, $request->getPort()); + $this->assertFalse($request->isSecure()); $request = Request::create('https://test.com:90/foo'); $this->assertEquals('https://test.com:90/foo', $request->getUri()); $this->assertEquals('/foo', $request->getPathInfo()); $this->assertEquals('test.com', $request->getHost()); + $this->assertEquals('test.com:90', $request->getHttpHost()); $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json); $this->assertEquals($json, $request->getContent()); + $this->assertFalse($request->isSecure()); } /** @@ -101,6 +110,7 @@ public function testDuplicate() /** * @covers Symfony\Component\HttpFoundation\Request::getFormat + * @covers Symfony\Component\HttpFoundation\Request::setFormat * @dataProvider getFormatToMimeTypeMapProvider */ public function testGetFormatFromMimeType($format, $mimeTypes) @@ -109,7 +119,10 @@ public function testGetFormatFromMimeType($format, $mimeTypes) foreach ($mimeTypes as $mime) { $this->assertEquals($format, $request->getFormat($mime)); } - + $request->setFormat($format, $mimeTypes); + foreach ($mimeTypes as $mime) { + $this->assertEquals($format, $request->getFormat($mime)); + } } /** @@ -322,6 +335,7 @@ public function testGetUriForPath() $request->initialize(array(), array(), array(), array(), array(), $server); $this->assertEquals('http://hostname/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite, default port without HOST_HEADER'); + $this->assertEquals('hostname', $request->getHttpHost()); } /** @@ -420,6 +434,23 @@ public function testGetSetMethod() $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); } + public function testGetClientIp() + { + $request = new Request; + $this->assertEquals('', $request->getClientIp()); + $this->assertEquals('', $request->getClientIp(true)); + $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '88.88.88.88')); + $this->assertEquals('88.88.88.88', $request->getClientIp()); + $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88')); + $this->assertEquals('127.0.0.1', $request->getClientIp()); + $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88')); + $this->assertEquals('88.88.88.88', $request->getClientIp(true)); + $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88')); + $this->assertEquals('127.0.0.1', $request->getClientIp()); + $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88')); + $this->assertEquals('88.88.88.88', $request->getClientIp(true)); + } + public function testGetContentWorksTwiceInDefaultMode() { $req = new Request; @@ -473,4 +504,163 @@ public function testCreateFromGlobals() unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']); } + + public function testOverrideGlobals() + { + $time = $_SERVER['REQUEST_TIME']; // fix for phpunit timer + + $request = new Request(); + $request->initialize(array('foo' => 'bar')); + $request->overrideGlobals(); + + $this->assertEquals(array('foo' => 'bar'), $_GET); + + $request->initialize(array(), array('foo' => 'bar')); + $request->overrideGlobals(); + + $this->assertEquals(array('foo' => 'bar'), $_POST); + + $this->assertArrayNotHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); + + $request->headers->set('X_FORWARDED_PROTO', 'https'); + + $this->assertTrue($request->isSecure()); + + $request->overrideGlobals(); + + $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); + + $_SERVER['REQUEST_TIME'] = $time; // fix for phpunit timer + } + + public function testGetScriptName() + { + $request = new Request(); + $this->assertEquals('', $request->getScriptName()); + + $server = array(); + $server['SCRIPT_NAME'] = '/index.php'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/index.php', $request->getScriptName()); + + $server = array(); + $server['ORIG_SCRIPT_NAME'] = '/frontend.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/frontend.php', $request->getScriptName()); + + $server = array(); + $server['SCRIPT_NAME'] = '/index.php'; + $server['ORIG_SCRIPT_NAME'] = '/frontend.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/index.php', $request->getScriptName()); + } + + public function testGetBasePath() + { + $request = new Request(); + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $server['SCRIPT_NAME'] = '/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $server['PHP_SELF'] = '/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $server['ORIG_SCRIPT_NAME'] = '/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('', $request->getBasePath()); + } + + public function testGetPreferredLanguage() + { + $request = new Request(); + $this->assertEquals('', $request->getPreferredLanguage()); + $this->assertEquals('fr', $request->getPreferredLanguage(array('fr'))); + $this->assertEquals('fr', $request->getPreferredLanguage(array('fr', 'en'))); + $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'fr'))); + $this->assertEquals('fr-ch', $request->getPreferredLanguage(array('fr-ch', 'fr-fr'))); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'en-us'))); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); + } + + public function testIsXmlHttpRequest() + { + $request = new Request(); + $this->assertFalse($request->isXmlHttpRequest()); + + $request->headers->set('X-Requested-With', 'XMLHttpRequest'); + $this->assertTrue($request->isXmlHttpRequest()); + + $request->headers->remove('X-Requested-With'); + $this->assertFalse($request->isXmlHttpRequest()); + } + + public function testGetCharsets() + { + $request = new Request(); + $this->assertEquals(array(), $request->getCharsets()); + $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6'); + $this->assertEquals(array(), $request->getCharsets()); // testing caching + + $request = new Request(); + $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6'); + $this->assertEquals(array('ISO-8859-1', 'US-ASCII', 'UTF-8', 'ISO-10646-UCS-2'), $request->getCharsets()); + + $request = new Request(); + $request->headers->set('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'); + $this->assertEquals(array('ISO-8859-1', '*', 'utf-8'), $request->getCharsets()); + } + + public function testGetAcceptableContentTypes() + { + $request = new Request(); + $this->assertEquals(array(), $request->getAcceptableContentTypes()); + $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*'); + $this->assertEquals(array(), $request->getAcceptableContentTypes()); // testing caching + + $request = new Request(); + $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*'); + $this->assertEquals(array('multipart/mixed', '*/*', 'text/html', 'application/xhtml+xml', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml', 'application/vnd.wap.wmlscriptc'), $request->getAcceptableContentTypes()); + } + + public function testGetLanguages() + { + $request = new Request(); + $this->assertNull($request->getLanguages()); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages()); + $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages()); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, i-cherokee; q=0.6'); + $this->assertEquals(array('zh', 'cherokee'), $request->getLanguages()); + } } diff --git a/tests/Symfony/Tests/Component/HttpKernel/Bundle/BundleTest.php b/tests/Symfony/Tests/Component/HttpKernel/Bundle/BundleTest.php deleted file mode 100644 index b3bf6fed30e6f..0000000000000 --- a/tests/Symfony/Tests/Component/HttpKernel/Bundle/BundleTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Tests\Component\HttpKernel\Bundle; - -class BundleTest extends \PHPUnit_Framework_TestCase -{ - public function testGetNormalizedPathReturnsANormalizedPath() - { - $bundle = $this - ->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') - ->setMethods(array('getPath')) - ->disableOriginalConstructor() - ->getMockForAbstractClass() - ; - - $bundle - ->expects($this->once()) - ->method('getPath') - ->will($this->returnValue('path\\to\\foo\\bar')) - ; - - $this->assertEquals('path/to/foo/bar', $bundle->getNormalizedPath()); - } -} diff --git a/tests/Symfony/Tests/Component/HttpKernel/CacheWarmer/CacheWarmerAggregateTest.php b/tests/Symfony/Tests/Component/HttpKernel/CacheWarmer/CacheWarmerAggregateTest.php new file mode 100644 index 0000000000000..4b75a4717392f --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpKernel/CacheWarmer/CacheWarmerAggregateTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpKernel\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; + +class CacheWarmerAggregateTest extends \PHPUnit_Framework_TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public function testInjectWarmersUsingConstructor() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + public function testInjectWarmersUsingAdd() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(); + $aggregate->add($warmer); + $aggregate->warmUp(self::$cacheDir); + } + + public function testInjectWarmersUsingSetWarmers() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(); + $aggregate->setWarmers(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->never()) + ->method('isOptional'); + $warmer + ->expects($this->once()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->enableOptionalWarmers(); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesNotCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsNotEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('isOptional') + ->will($this->returnValue(true)); + $warmer + ->expects($this->never()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + protected function getCacheWarmerMock() + { + $warmer = $this->getMockBuilder('Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface') + ->disableOriginalConstructor() + ->getMock(); + + return $warmer; + } +} diff --git a/tests/Symfony/Tests/Component/HttpKernel/CacheWarmer/CacheWarmerTest.php b/tests/Symfony/Tests/Component/HttpKernel/CacheWarmer/CacheWarmerTest.php new file mode 100644 index 0000000000000..063a114978da0 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpKernel/CacheWarmer/CacheWarmerTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpKernel\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; + +class CacheWarmerTest extends \PHPUnit_Framework_TestCase +{ + protected static $cacheFile; + + public static function setUpBeforeClass() + { + self::$cacheFile = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheFile); + } + + public function testWriteCacheFileCreatesTheFile() + { + $warmer = new TestCacheWarmer(self::$cacheFile); + $warmer->warmUp(dirname(self::$cacheFile)); + + $this->assertTrue(file_exists(self::$cacheFile)); + } + + /** + * @expectedException \RuntimeException + */ + public function testWriteNonWritableCacheFileThrowsARuntimeException() + { + $nonWritableFile = '/this/file/is/very/probably/not/writable'; + $warmer = new TestCacheWarmer($nonWritableFile); + $warmer->warmUp(dirname($nonWritableFile)); + } +} + +class TestCacheWarmer extends CacheWarmer +{ + protected $file; + + public function __construct($file) + { + $this->file = $file; + } + + public function warmUp($cacheDir) + { + $this->writeCacheFile($this->file, 'content'); + } + + public function isOptional() + { + return false; + } +} diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php index 4c0d7cfde9e42..d6a5ba7f2afaf 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTest.php @@ -893,4 +893,67 @@ public function testShouldNotCatchExceptions() $this->assertExceptionsAreNotCaught(); } + + public function testEsiCacheSendsTheLowestTtl() + { + $responses = array( + array( + 'status' => 200, + 'body' => ' ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array('Cache-Control' => 's-maxage=300'), + ), + array( + 'status' => 200, + 'body' => 'My name is Bobby.', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals("Hello World! My name is Bobby.", $this->response->getContent()); + $this->assertEquals(100, $this->response->getTtl()); + } + + public function testEsiCacheForceValidation() + { + $responses = array( + array( + 'status' => 200, + 'body' => ' ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array('ETag' => 'foobar'), + ), + array( + 'status' => 200, + 'body' => 'My name is Bobby.', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent()); + $this->assertEquals(null, $this->response->getTtl()); + $this->assertTrue($this->response->mustRevalidate()); + $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache')); + } } diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTestCase.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTestCase.php index 1af5811cc1808..a7654a5e162f5 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTestCase.php +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/HttpCacheTestCase.php @@ -12,8 +12,10 @@ namespace Symfony\Tests\Component\HttpKernel\HttpCache; require_once __DIR__.'/TestHttpKernel.php'; +require_once __DIR__.'/TestMultipleHttpKernel.php'; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpCache\Esi; use Symfony\Component\HttpKernel\HttpCache\HttpCache; use Symfony\Component\HttpKernel\HttpCache\Store; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -28,12 +30,14 @@ class HttpCacheTestCase extends \PHPUnit_Framework_TestCase protected $response; protected $responses; protected $catch; + protected $esi; protected function setUp() { $this->kernel = null; $this->cache = null; + $this->esi = null; $this->caches = array(); $this->cacheConfig = array(); @@ -41,6 +45,7 @@ protected function setUp() $this->response = null; $this->responses = array(); + $this->catch = false; $this->clearDirectory(sys_get_temp_dir().'/http_cache'); @@ -101,7 +106,7 @@ public function assertExceptionsAreNotCaught() $this->assertFalse($this->kernel->isCatchingExceptions()); } - public function request($method, $uri = '/', $server = array(), $cookies = array()) + public function request($method, $uri = '/', $server = array(), $cookies = array(), $esi = false) { if (null === $this->kernel) { throw new \LogicException('You must call setNextResponse() before calling request().'); @@ -112,7 +117,9 @@ public function request($method, $uri = '/', $server = array(), $cookies = array $this->store = new Store(sys_get_temp_dir().'/http_cache'); $this->cacheConfig['debug'] = true; - $this->cache = new HttpCache($this->kernel, $this->store, null, $this->cacheConfig); + + $this->esi = $esi ? new Esi() : null; + $this->cache = new HttpCache($this->kernel, $this->store, $this->esi, $this->cacheConfig); $this->request = Request::create($uri, $method, array(), $cookies, array(), $server); $this->response = $this->cache->handle($this->request, HttpKernelInterface::MASTER_REQUEST, $this->catch); @@ -133,11 +140,14 @@ public function getMetaStorageValues() // A basic response with 200 status code and a tiny body. public function setNextResponse($statusCode = 200, array $headers = array(), $body = 'Hello World', \Closure $customizer = null) { - $called = false; - $this->kernel = new TestHttpKernel($body, $statusCode, $headers, $customizer); } + public function setNextResponses($responses) + { + $this->kernel = new TestMultipleHttpKernel($responses); + } + public function catchExceptions($catch = true) { $this->catch = $catch; diff --git a/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php new file mode 100644 index 0000000000000..899e8c2b57dd7 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpKernel/HttpCache/TestMultipleHttpKernel.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; + +class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInterface +{ + protected $bodies; + protected $statuses; + protected $headers; + protected $catch; + protected $call; + + public function __construct($responses) + { + $this->bodies = array(); + $this->statuses = array(); + $this->headers = array(); + $this->call = false; + + foreach ($responses as $response) { + $this->bodies[] = $response['body']; + $this->statuses[] = $response['status']; + $this->headers[] = $response['headers']; + } + + parent::__construct(new EventDispatcher(), $this); + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) + { + return parent::handle($request, $type, $catch); + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + $this->called = true; + + $response = new Response(array_shift($this->bodies), array_shift($this->statuses), array_shift($this->headers)); + + return $response; + } + + public function hasBeenCalled() + { + return $this->called; + } + + public function reset() + { + $this->call = false; + } +} diff --git a/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php b/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php index 5695f5a6fdbaf..18542f50f7a30 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php @@ -13,10 +13,296 @@ use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\Loader\LoaderInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; class KernelTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $this->assertEquals($env, $kernel->getEnvironment()); + $this->assertEquals($debug, $kernel->isDebug()); + $this->assertFalse($kernel->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $kernel->getStartTime()); + $this->assertNull($kernel->getContainer()); + } + + public function testClone() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $clone = clone $kernel; + + $this->assertEquals($env, $clone->getEnvironment()); + $this->assertEquals($debug, $clone->isDebug()); + $this->assertFalse($clone->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $clone->getStartTime()); + $this->assertNull($clone->getContainer()); + } + + public function testBootInitializesBundlesAndContainer() + { + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('initializeBundles'); + $kernel->expects($this->once()) + ->method('initializeContainer'); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel->boot(); + } + + public function testBootSetsTheContainerToTheBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') + ->disableOriginalConstructor() + ->getMock(); + $bundle->expects($this->once()) + ->method('setContainer'); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + } + + public function testBootSetsTheBootedFlagToTrue() + { + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel->boot(); + + $this->assertTrue($kernel->isBooted()); + } + + public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce() + { + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('initializeBundles', 'initializeContainer', 'getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel->boot(); + $kernel->boot(); + } + + public function testShutdownCallsShutdownOnAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') + ->disableOriginalConstructor() + ->getMock(); + $bundle->expects($this->once()) + ->method('shutdown'); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->shutdown(); + } + + public function testShutdownGivesNullContainerToAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle') + ->disableOriginalConstructor() + ->getMock(); + $bundle->expects($this->once()) + ->method('setContainer') + ->with(null); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->shutdown(); + } + + public function testHandleCallsHandleOnHttpKernel() + { + $type = HttpKernelInterface::MASTER_REQUEST; + $catch = true; + $request = new Request(); + + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->getMock(); + $httpKernelMock + ->expects($this->once()) + ->method('handle') + ->with($request, $type, $catch); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel')) + ->getMock(); + + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->handle($request, $type, $catch); + } + + public function testHandleBootsTheKernel() + { + $type = HttpKernelInterface::MASTER_REQUEST; + $catch = true; + $request = new Request(); + + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->getMock(); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getHttpKernel', 'boot')) + ->getMock(); + + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->expects($this->once()) + ->method('boot'); + + // required as this value is initialized + // in the kernel constructor, which we don't call + $kernel->setIsBooted(false); + + $kernel->handle($request, $type, $catch); + } + + public function testStripComments() + { + if (!function_exists('token_get_all')) { + $this->markTestSkipped(); + return; + } + $source = <<assertEquals($expected, Kernel::stripComments($source)); + } + + public function testIsClassInActiveBundleFalse() + { + $kernel = $this->getKernelMockForIsClassInActiveBundleTest(); + + $this->assertFalse($kernel->isClassInActiveBundle('Not\In\Active\Bundle')); + } + + public function testIsClassInActiveBundleFalseNoNamespace() + { + $kernel = $this->getKernelMockForIsClassInActiveBundleTest(); + + $this->assertFalse($kernel->isClassInActiveBundle('NotNamespacedClass')); + } + + public function testIsClassInActiveBundleTrue() + { + $kernel = $this->getKernelMockForIsClassInActiveBundleTest(); + + $this->assertTrue($kernel->isClassInActiveBundle(__NAMESPACE__.'\FooBarBundle\SomeClass')); + } + + protected function getKernelMockForIsClassInActiveBundleTest() + { + $bundle = new FooBarBundle(); + + $kernel = $this->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getBundles')) + ->getMock(); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + return $kernel; + } + + public function testGetRootDir() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals(__DIR__, $kernel->getRootDir()); + } + + public function testGetName() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals('HttpKernel', $kernel->getName()); + } + + public function testSerialize() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $expected = serialize(array($env, $debug)); + $this->assertEquals($expected, $kernel->serialize()); + } + /** * @expectedException \InvalidArgumentException */ @@ -241,21 +527,21 @@ public function testInitializeBundleThrowsExceptionWhenRegisteringTwoBundlesWith { $fooBundle = $this->getBundle(null, null, 'FooBundle', 'DuplicateName'); $barBundle = $this->getBundle(null, null, 'BarBundle', 'DuplicateName'); - + $kernel = $this->getKernel(); $kernel ->expects($this->once()) ->method('registerBundles') ->will($this->returnValue(array($fooBundle, $barBundle))) ; - $kernel->initializeBundles(); + $kernel->initializeBundles(); } protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) { $bundle = $this ->getMockBuilder('Symfony\Tests\Component\HttpKernel\BundleForTest') - ->setMethods(array('getNormalizedPath', 'getParent', 'getName')) + ->setMethods(array('getPath', 'getParent', 'getName')) ->disableOriginalConstructor() ; @@ -273,16 +559,16 @@ protected function getBundle($dir = null, $parent = null, $className = null, $bu $bundle ->expects($this->any()) - ->method('getNormalizedPath') + ->method('getPath') ->will($this->returnValue(strtr($dir, '\\', '/'))) ; - + $bundle ->expects($this->any()) ->method('getParent') ->will($this->returnValue($parent)) ; - + return $bundle; } @@ -315,6 +601,7 @@ public function getBundleMap() public function registerRootDir() { + return __DIR__; } public function registerBundles() @@ -333,9 +620,24 @@ public function initializeBundles() { parent::initializeBundles(); } + + public function isBooted() + { + return $this->booted; + } + + public function setIsBooted($value) + { + $this->booted = (bool) $value; + } } abstract class BundleForTest implements BundleInterface { // We can not extend Symfony\Component\HttpKernel\Bundle\Bundle as we want to mock getName() which is final -} \ No newline at end of file +} + +class FooBarBundle extends Bundle +{ + // We need a full namespaced bundle instance to test isClassInActiveBundle +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy