Merge branch 'master' of github.com:symfony/symfony into deprecationErrors
This commit is contained in:
commit
6b105504f4
|
@ -7,7 +7,7 @@ in 2.1 minor versions.
|
||||||
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
||||||
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.1.0...v2.1.1
|
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.1.0...v2.1.1
|
||||||
|
|
||||||
* 2.1.4 (2012-12-29)
|
* 2.1.4 (2012-11-29)
|
||||||
|
|
||||||
* e5536f0: replaced magic strings by proper constants
|
* e5536f0: replaced magic strings by proper constants
|
||||||
* 6a3ba52: fixed the logic in Request::isSecure() (if the information comes from a source that we trust, don't check other ones)
|
* 6a3ba52: fixed the logic in Request::isSecure() (if the information comes from a source that we trust, don't check other ones)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
README
|
README
|
||||||
======
|
======
|
||||||
|
|
||||||
[![Build Status](https://secure.travis-ci.org/symfony/symfony.png?branch=master)](http://travis-ci.org/symfony/symfony)
|
|
||||||
|
|
||||||
What is Symfony2?
|
What is Symfony2?
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,65 @@
|
||||||
|
|
||||||
* The PasswordType is now not trimmed by default.
|
* The PasswordType is now not trimmed by default.
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
|
||||||
|
* RouteCollection does not behave like a tree structure anymore but as a flat
|
||||||
|
array of Routes. So when using PHP to build the RouteCollection, you must
|
||||||
|
make sure to add routes to the sub-collection before adding it to the parent
|
||||||
|
collection (this is not relevant when using YAML or XML for Route definitions).
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
$rootCollection = new RouteCollection();
|
||||||
|
$subCollection = new RouteCollection();
|
||||||
|
$rootCollection->addCollection($subCollection);
|
||||||
|
$subCollection->add('foo', new Route('/foo'));
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
$rootCollection = new RouteCollection();
|
||||||
|
$subCollection = new RouteCollection();
|
||||||
|
$subCollection->add('foo', new Route('/foo'));
|
||||||
|
$rootCollection->addCollection($subCollection);
|
||||||
|
```
|
||||||
|
|
||||||
|
Also one must call `addCollection` from the bottom to the top hierarchy.
|
||||||
|
So the correct sequence is the following (and not the reverse):
|
||||||
|
|
||||||
|
```
|
||||||
|
$childCollection->->addCollection($grandchildCollection);
|
||||||
|
$rootCollection->addCollection($childCollection);
|
||||||
|
```
|
||||||
|
|
||||||
|
* The methods `RouteCollection::getParent()` and `RouteCollection::getRoot()`
|
||||||
|
have been deprecated and will be removed in Symfony 2.3.
|
||||||
|
* Misusing the `RouteCollection::addPrefix` method to add defaults, requirements
|
||||||
|
or options without adding a prefix is not supported anymore. So if you called `addPrefix`
|
||||||
|
with an empty prefix or `/` only (both have no relevance), like
|
||||||
|
`addPrefix('', $defaultsArray, $requirementsArray, $optionsArray)`
|
||||||
|
you need to use the new dedicated methods `addDefaults($defaultsArray)`,
|
||||||
|
`addRequirements($requirementsArray)` or `addOptions($optionsArray)` instead.
|
||||||
|
* The `$options` parameter to `RouteCollection::addPrefix()` has been deprecated
|
||||||
|
because adding options has nothing to do with adding a path prefix. If you want to add options
|
||||||
|
to all child routes of a RouteCollection, you can use `addOptions()`.
|
||||||
|
* The method `RouteCollection::getPrefix()` has been deprecated
|
||||||
|
because it suggested that all routes in the collection would have this prefix, which is
|
||||||
|
not necessarily true. On top of that, since there is no tree structure anymore, this method
|
||||||
|
is also useless.
|
||||||
|
* `RouteCollection::addCollection(RouteCollection $collection)` should now only be
|
||||||
|
used with a single parameter. The other params `$prefix`, `$default`, `$requirements` and `$options`
|
||||||
|
will still work, but have been deprecated. The `addPrefix` method should be used for this
|
||||||
|
use-case instead.
|
||||||
|
Before: `$parentCollection->addCollection($collection, '/prefix', array(...), array(...))`
|
||||||
|
After:
|
||||||
|
```
|
||||||
|
$collection->addPrefix('/prefix', array(...), array(...));
|
||||||
|
$parentCollection->addCollection($collection);
|
||||||
|
```
|
||||||
|
|
||||||
### Validator
|
### Validator
|
||||||
|
|
||||||
* Interfaces were created for the classes `ConstraintViolation`,
|
* Interfaces were created for the classes `ConstraintViolation`,
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.3",
|
"php": ">=5.3.3",
|
||||||
"doctrine/common": ">2.2,<2.4-dev",
|
"doctrine/common": ">2.2,<2.4-dev",
|
||||||
"twig/twig": ">=1.9.1,<2.0-dev"
|
"twig/twig": ">=1.11.0,<2.0-dev"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
"symfony/browser-kit": "self.version",
|
"symfony/browser-kit": "self.version",
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
2.1.5
|
||||||
|
-----
|
||||||
|
|
||||||
|
* fixed caching of choice lists when EntityType is used with the "query_builder" option
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -392,7 +392,10 @@ class EntityChoiceList extends ObjectChoiceList
|
||||||
private function getIdentifierValues($entity)
|
private function getIdentifierValues($entity)
|
||||||
{
|
{
|
||||||
if (!$this->em->contains($entity)) {
|
if (!$this->em->contains($entity)) {
|
||||||
throw new FormException('Entities passed to the choice field must be managed');
|
throw new FormException(
|
||||||
|
'Entities passed to the choice field must be managed. Maybe ' .
|
||||||
|
'persist them in the entity manager?'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->em->initializeObject($entity);
|
$this->em->initializeObject($entity);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Symfony\Bridge\Doctrine\Form\Type;
|
namespace Symfony\Bridge\Doctrine\Form\Type;
|
||||||
|
|
||||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||||
|
use Symfony\Component\Form\Exception\FormException;
|
||||||
use Doctrine\Common\Persistence\ObjectManager;
|
use Doctrine\Common\Persistence\ObjectManager;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
||||||
|
@ -76,16 +77,16 @@ abstract class DoctrineType extends AbstractType
|
||||||
// A second parameter ($key) is passed, so we cannot use
|
// A second parameter ($key) is passed, so we cannot use
|
||||||
// spl_object_hash() directly (which strictly requires
|
// spl_object_hash() directly (which strictly requires
|
||||||
// one parameter)
|
// one parameter)
|
||||||
array_walk_recursive($choiceHashes, function ($value) {
|
array_walk_recursive($choiceHashes, function (&$value) {
|
||||||
return spl_object_hash($value);
|
$value = spl_object_hash($value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$preferredChoiceHashes = $options['preferred_choices'];
|
$preferredChoiceHashes = $options['preferred_choices'];
|
||||||
|
|
||||||
if (is_array($preferredChoiceHashes)) {
|
if (is_array($preferredChoiceHashes)) {
|
||||||
array_walk_recursive($preferredChoiceHashes, function ($value) {
|
array_walk_recursive($preferredChoiceHashes, function (&$value) {
|
||||||
return spl_object_hash($value);
|
$value = spl_object_hash($value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +131,17 @@ abstract class DoctrineType extends AbstractType
|
||||||
return $registry->getManager($em);
|
return $registry->getManager($em);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $registry->getManagerForClass($options['class']);
|
$em = $registry->getManagerForClass($options['class']);
|
||||||
|
|
||||||
|
if (null === $em) {
|
||||||
|
throw new FormException(sprintf(
|
||||||
|
'Class "%s" seems not to be a managed Doctrine entity. ' .
|
||||||
|
'Did you forget to map it?',
|
||||||
|
$options['class']
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $em;
|
||||||
};
|
};
|
||||||
|
|
||||||
$resolver->setDefaults(array(
|
$resolver->setDefaults(array(
|
||||||
|
|
|
@ -11,26 +11,53 @@
|
||||||
|
|
||||||
namespace Symfony\Bridge\Doctrine\Form\Type;
|
namespace Symfony\Bridge\Doctrine\Form\Type;
|
||||||
|
|
||||||
use Doctrine\Common\Persistence\ObjectManager;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader;
|
use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader;
|
||||||
|
use Doctrine\Common\Persistence\ObjectManager;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
|
||||||
class EntityType extends DoctrineType
|
class EntityType extends DoctrineType
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $loaderCache = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the default loader object.
|
* Return the default loader object.
|
||||||
*
|
*
|
||||||
* @param ObjectManager $manager
|
* @param ObjectManager $manager
|
||||||
* @param mixed $queryBuilder
|
* @param QueryBuilder|\Closure $queryBuilder
|
||||||
* @param string $class
|
* @param string $class
|
||||||
|
*
|
||||||
* @return ORMQueryBuilderLoader
|
* @return ORMQueryBuilderLoader
|
||||||
|
*
|
||||||
|
* @throws UnexpectedTypeException If the passed $queryBuilder is no \Closure
|
||||||
|
* and no QueryBuilder or if the closure
|
||||||
|
* does not return a QueryBuilder.
|
||||||
*/
|
*/
|
||||||
public function getLoader(ObjectManager $manager, $queryBuilder, $class)
|
public function getLoader(ObjectManager $manager, $queryBuilder, $class)
|
||||||
{
|
{
|
||||||
return new ORMQueryBuilderLoader(
|
if ($queryBuilder instanceof \Closure) {
|
||||||
$queryBuilder,
|
$queryBuilder = $queryBuilder($manager->getRepository($class));
|
||||||
$manager,
|
|
||||||
$class
|
if (!$queryBuilder instanceof QueryBuilder) {
|
||||||
);
|
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||||
|
}
|
||||||
|
} elseif (!$queryBuilder instanceof QueryBuilder) {
|
||||||
|
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure');
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is important to return the same loader for identical queries,
|
||||||
|
// otherwise the caching mechanism in DoctrineType does not work
|
||||||
|
// (which expects identical loaders for the cache to work)
|
||||||
|
$hash = md5($queryBuilder->getQuery()->getDQL());
|
||||||
|
|
||||||
|
if (!isset($this->loaderCache[$hash])) {
|
||||||
|
$this->loaderCache[$hash] = new ORMQueryBuilderLoader($queryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->loaderCache[$hash];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName()
|
public function getName()
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Symfony\Bridge\Doctrine\Tests\Form\Type;
|
namespace Symfony\Bridge\Doctrine\Tests\Form\Type;
|
||||||
|
|
||||||
use Symfony\Component\Form\Tests\FormPerformanceTestCase;
|
use Symfony\Component\Form\Tests\FormPerformanceTestCase;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity;
|
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity;
|
||||||
use Doctrine\ORM\Tools\SchemaTool;
|
use Doctrine\ORM\Tools\SchemaTool;
|
||||||
use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase;
|
use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase;
|
||||||
|
@ -36,6 +37,9 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase
|
||||||
$manager->expects($this->any())
|
$manager->expects($this->any())
|
||||||
->method('getManager')
|
->method('getManager')
|
||||||
->will($this->returnValue($this->em));
|
->will($this->returnValue($this->em));
|
||||||
|
$manager->expects($this->any())
|
||||||
|
->method('getManagerForClass')
|
||||||
|
->will($this->returnValue($this->em));
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
new CoreExtension(),
|
new CoreExtension(),
|
||||||
|
@ -100,7 +104,7 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase
|
||||||
{
|
{
|
||||||
$this->setMaxRunningTime(1);
|
$this->setMaxRunningTime(1);
|
||||||
|
|
||||||
for ($i = 0; $i < 20; ++$i) {
|
for ($i = 0; $i < 40; ++$i) {
|
||||||
$form = $this->factory->create('entity', null, array(
|
$form = $this->factory->create('entity', null, array(
|
||||||
'class' => self::ENTITY_CLASS,
|
'class' => self::ENTITY_CLASS,
|
||||||
));
|
));
|
||||||
|
@ -109,4 +113,62 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase
|
||||||
$form->createView();
|
$form->createView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group benchmark
|
||||||
|
*/
|
||||||
|
public function testCollapsedEntityFieldWithQueryBuilder()
|
||||||
|
{
|
||||||
|
$this->setMaxRunningTime(1);
|
||||||
|
|
||||||
|
for ($i = 0; $i < 40; ++$i) {
|
||||||
|
$form = $this->factory->create('entity', null, array(
|
||||||
|
'class' => self::ENTITY_CLASS,
|
||||||
|
'query_builder' => function (EntityRepository $repo) {
|
||||||
|
return $repo->createQueryBuilder('e')->addOrderBy('e.id', 'DESC');
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
// force loading of the choice list
|
||||||
|
$form->createView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group benchmark
|
||||||
|
*/
|
||||||
|
public function testCollapsedEntityFieldWithChoices()
|
||||||
|
{
|
||||||
|
$choices = $this->em->createQuery('SELECT c FROM ' . self::ENTITY_CLASS . ' c')->getResult();
|
||||||
|
$this->setMaxRunningTime(1);
|
||||||
|
|
||||||
|
for ($i = 0; $i < 40; ++$i) {
|
||||||
|
$form = $this->factory->create('entity', null, array(
|
||||||
|
'class' => self::ENTITY_CLASS,
|
||||||
|
'choices' => $choices,
|
||||||
|
));
|
||||||
|
|
||||||
|
// force loading of the choice list
|
||||||
|
$form->createView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group benchmark
|
||||||
|
*/
|
||||||
|
public function testCollapsedEntityFieldWithPreferredChoices()
|
||||||
|
{
|
||||||
|
$choices = $this->em->createQuery('SELECT c FROM ' . self::ENTITY_CLASS . ' c')->getResult();
|
||||||
|
$this->setMaxRunningTime(1);
|
||||||
|
|
||||||
|
for ($i = 0; $i < 40; ++$i) {
|
||||||
|
$form = $this->factory->create('entity', null, array(
|
||||||
|
'class' => self::ENTITY_CLASS,
|
||||||
|
'preferred_choices' => $choices,
|
||||||
|
));
|
||||||
|
|
||||||
|
// force loading of the choice list
|
||||||
|
$form->createView();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
namespace Symfony\Bridge\Doctrine\Tests\Logger;
|
namespace Symfony\Bridge\Doctrine\Tests\Logger;
|
||||||
|
|
||||||
|
|
||||||
class DbalLoggerTest extends \PHPUnit_Framework_TestCase
|
class DbalLoggerTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -45,6 +45,13 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
*/
|
*/
|
||||||
private $loaded = false;
|
private $loaded = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use the identifier for index generation
|
||||||
|
*
|
||||||
|
* @var Boolean
|
||||||
|
*/
|
||||||
|
private $identifierAsIndex = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $class
|
* @param string $class
|
||||||
* @param string $labelPath
|
* @param string $labelPath
|
||||||
|
@ -69,6 +76,10 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
$choices = array();
|
$choices = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (1 === count($this->identifier) && $this->isInteger(current($this->identifier))) {
|
||||||
|
$this->identifierAsIndex = true;
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct($choices, $labelPath, array(), $groupPath);
|
parent::__construct($choices, $labelPath, array(), $groupPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +235,7 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
// know that the IDs are used as indices
|
// know that the IDs are used as indices
|
||||||
|
|
||||||
// Attention: This optimization does not check choices for existence
|
// Attention: This optimization does not check choices for existence
|
||||||
if (1 === count($this->identifier)) {
|
if ($this->identifierAsIndex) {
|
||||||
$indices = array();
|
$indices = array();
|
||||||
|
|
||||||
foreach ($models as $model) {
|
foreach ($models as $model) {
|
||||||
|
@ -259,7 +270,7 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
// know that the IDs are used as indices and values
|
// know that the IDs are used as indices and values
|
||||||
|
|
||||||
// Attention: This optimization does not check values for existence
|
// Attention: This optimization does not check values for existence
|
||||||
if (1 === count($this->identifier)) {
|
if ($this->identifierAsIndex) {
|
||||||
return $this->fixIndices($values);
|
return $this->fixIndices($values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +294,7 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
*/
|
*/
|
||||||
protected function createIndex($model)
|
protected function createIndex($model)
|
||||||
{
|
{
|
||||||
if (1 === count($this->identifier)) {
|
if ($this->identifierAsIndex) {
|
||||||
return current($this->getIdentifierValues($model));
|
return current($this->getIdentifierValues($model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +347,7 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
* exception is thrown.
|
* exception is thrown.
|
||||||
*
|
*
|
||||||
* @param object $model The model for which to get the identifier
|
* @param object $model The model for which to get the identifier
|
||||||
|
*
|
||||||
* @throws FormException If the model does not exist
|
* @throws FormException If the model does not exist
|
||||||
*/
|
*/
|
||||||
private function getIdentifierValues($model)
|
private function getIdentifierValues($model)
|
||||||
|
@ -345,10 +357,22 @@ class ModelChoiceList extends ObjectChoiceList
|
||||||
}
|
}
|
||||||
|
|
||||||
// readonly="true" models do not implement Persistent.
|
// readonly="true" models do not implement Persistent.
|
||||||
if ($model instanceof BaseObject and method_exists($model, 'getPrimaryKey')) {
|
if ($model instanceof BaseObject && method_exists($model, 'getPrimaryKey')) {
|
||||||
return array($model->getPrimaryKey());
|
return array($model->getPrimaryKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $model->getPrimaryKeys();
|
return $model->getPrimaryKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this column in an integer
|
||||||
|
*
|
||||||
|
* @param ColumnMap $column
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private function isInteger(\ColumnMap $column)
|
||||||
|
{
|
||||||
|
return $column->getPdoType() === \PDO::PARAM_INT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ class TranslationCollectionFormListener implements EventSubscriberInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$foundData) {
|
if (!$foundData) {
|
||||||
throw new UnexpectedTypeException($rootData, 'Propel i18n object');;
|
throw new UnexpectedTypeException($rootData, 'Propel i18n object');
|
||||||
}
|
}
|
||||||
|
|
||||||
$newTranslation = new $this->i18nClass();
|
$newTranslation = new $this->i18nClass();
|
||||||
|
|
|
@ -31,7 +31,10 @@ class ItemQuery
|
||||||
|
|
||||||
public function getPrimaryKeys()
|
public function getPrimaryKeys()
|
||||||
{
|
{
|
||||||
return array('id');
|
$cm = new \ColumnMap('id', new \TableMap());
|
||||||
|
$cm->setType('INTEGER');
|
||||||
|
|
||||||
|
return array('id' => $cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,6 +22,9 @@ class ReadOnlyItemQuery
|
||||||
|
|
||||||
public function getPrimaryKeys()
|
public function getPrimaryKeys()
|
||||||
{
|
{
|
||||||
return array('id');
|
$cm = new \ColumnMap('id', new \TableMap());
|
||||||
|
$cm->setType('INTEGER');
|
||||||
|
|
||||||
|
return array('id' => $cm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,7 @@ class TranslatableItem implements \Persistent
|
||||||
|
|
||||||
public function addTranslatableItemI18n(TranslatableItemI18n $i)
|
public function addTranslatableItemI18n(TranslatableItemI18n $i)
|
||||||
{
|
{
|
||||||
if(!in_array($i, $this->currentTranslations))
|
if (!in_array($i, $this->currentTranslations)) {
|
||||||
{
|
|
||||||
$this->currentTranslations[$i->getLocale()] = $i;
|
$this->currentTranslations[$i->getLocale()] = $i;
|
||||||
$i->setItem($this);
|
$i->setItem($this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace Symfony\Bridge\Propel1\Tests\Fixtures;
|
||||||
|
|
||||||
use PropelPDO;
|
use PropelPDO;
|
||||||
|
|
||||||
class TranslatableItemI18n implements \Persistent {
|
class TranslatableItemI18n implements \Persistent
|
||||||
|
{
|
||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
private $locale;
|
private $locale;
|
||||||
|
@ -100,7 +100,6 @@ class TranslatableItemI18n implements \Persistent {
|
||||||
|
|
||||||
public function getLocale()
|
public function getLocale()
|
||||||
{
|
{
|
||||||
|
|
||||||
return $this->locale;
|
return $this->locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +121,6 @@ class TranslatableItemI18n implements \Persistent {
|
||||||
|
|
||||||
public function getValue()
|
public function getValue()
|
||||||
{
|
{
|
||||||
|
|
||||||
return $this->value;
|
return $this->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +132,6 @@ class TranslatableItemI18n implements \Persistent {
|
||||||
|
|
||||||
public function getValue2()
|
public function getValue2()
|
||||||
{
|
{
|
||||||
|
|
||||||
return $this->value2;
|
return $this->value2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Bundle\TwigBundle\Extension;
|
namespace Symfony\Bridge\Twig\Extension;
|
||||||
|
|
||||||
if (!defined('ENT_SUBSTITUTE')) {
|
if (!defined('ENT_SUBSTITUTE')) {
|
||||||
define('ENT_SUBSTITUTE', 8);
|
define('ENT_SUBSTITUTE', 8);
|
|
@ -221,6 +221,7 @@
|
||||||
|
|
||||||
{% block form_label %}
|
{% block form_label %}
|
||||||
{% spaceless %}
|
{% spaceless %}
|
||||||
|
{% if label is not sameas(false) %}
|
||||||
{% if not compound %}
|
{% if not compound %}
|
||||||
{% set label_attr = label_attr|merge({'for': id}) %}
|
{% set label_attr = label_attr|merge({'for': id}) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -231,6 +232,7 @@
|
||||||
{% set label = name|humanize %}
|
{% set label = name|humanize %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
|
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
|
||||||
|
{% endif %}
|
||||||
{% endspaceless %}
|
{% endspaceless %}
|
||||||
{% endblock form_label %}
|
{% endblock form_label %}
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Bundle\TwigBundle\Tests\Extension;
|
namespace Symfony\Bridge\Twig\Tests\Extension;
|
||||||
|
|
||||||
use Symfony\Bundle\TwigBundle\Extension\CodeExtension;
|
use Symfony\Bridge\Twig\Extension\CodeExtension;
|
||||||
|
|
||||||
class CodeExtensionTest extends \PHPUnit_Framework_TestCase
|
class CodeExtensionTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
|
@ -10,6 +10,7 @@ CHANGELOG
|
||||||
* A new parameter has been added to the DIC: `router.request_context.base_url`
|
* A new parameter has been added to the DIC: `router.request_context.base_url`
|
||||||
You can customize it for your functional tests or for generating urls with
|
You can customize it for your functional tests or for generating urls with
|
||||||
the right base url when your are in the cli context.
|
the right base url when your are in the cli context.
|
||||||
|
* Added support for default templates per render tag
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -89,6 +89,7 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->outputTags($output, $input->getOption('show-private'));
|
$this->outputTags($output, $input->getOption('show-private'));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,9 +92,7 @@ EOF
|
||||||
? implode(', ', $requirements['_method']) : $requirements['_method']
|
? implode(', ', $requirements['_method']) : $requirements['_method']
|
||||||
)
|
)
|
||||||
: 'ANY';
|
: 'ANY';
|
||||||
$hostname = '' !== $route->getHostnamePattern()
|
$hostname = '' !== $route->getHostnamePattern() ? $route->getHostnamePattern() : 'ANY';
|
||||||
? $route->getHostnamePattern() : 'ANY';
|
|
||||||
|
|
||||||
$maxName = max($maxName, strlen($name));
|
$maxName = max($maxName, strlen($name));
|
||||||
$maxMethod = max($maxMethod, strlen($method));
|
$maxMethod = max($maxMethod, strlen($method));
|
||||||
$maxHostname = max($maxHostname, strlen($hostname));
|
$maxHostname = max($maxHostname, strlen($hostname));
|
||||||
|
@ -111,8 +109,7 @@ EOF
|
||||||
? implode(', ', $requirements['_method']) : $requirements['_method']
|
? implode(', ', $requirements['_method']) : $requirements['_method']
|
||||||
)
|
)
|
||||||
: 'ANY';
|
: 'ANY';
|
||||||
$hostname = '' !== $route->getHostnamePattern()
|
$hostname = '' !== $route->getHostnamePattern() ? $route->getHostnamePattern() : 'ANY';
|
||||||
? $route->getHostnamePattern() : 'ANY';
|
|
||||||
$output->writeln(sprintf($format, $name, $method, $hostname, $route->getPattern()));
|
$output->writeln(sprintf($format, $name, $method, $hostname, $route->getPattern()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,10 +124,13 @@ EOF
|
||||||
throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
|
throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hostname = '' !== $route->getHostnamePattern() ? $route->getHostnamePattern() : 'ANY';
|
||||||
|
|
||||||
$output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
|
$output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
|
||||||
|
|
||||||
$output->writeln(sprintf('<comment>Name</comment> %s', $name));
|
$output->writeln(sprintf('<comment>Name</comment> %s', $name));
|
||||||
$output->writeln(sprintf('<comment>Pattern</comment> %s', $route->getPattern()));
|
$output->writeln(sprintf('<comment>Pattern</comment> %s', $route->getPattern()));
|
||||||
|
$output->writeln(sprintf('<comment>HostnamePattern</comment> %s', $hostname));
|
||||||
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($route)));
|
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($route)));
|
||||||
|
|
||||||
$defaults = '';
|
$defaults = '';
|
||||||
|
@ -147,6 +147,7 @@ EOF
|
||||||
foreach ($r as $name => $value) {
|
foreach ($r as $name => $value) {
|
||||||
$requirements .= ($requirements ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
$requirements .= ($requirements ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
||||||
}
|
}
|
||||||
|
$requirements = '' !== $requirements ? $requirements : 'NONE';
|
||||||
$output->writeln(sprintf('<comment>Requirements</comment> %s', $requirements));
|
$output->writeln(sprintf('<comment>Requirements</comment> %s', $requirements));
|
||||||
|
|
||||||
$options = '';
|
$options = '';
|
||||||
|
|
|
@ -78,7 +78,7 @@ class Controller extends ContainerAware
|
||||||
* @param string $view The view name
|
* @param string $view The view name
|
||||||
* @param array $parameters An array of parameters to pass to the view
|
* @param array $parameters An array of parameters to pass to the view
|
||||||
*
|
*
|
||||||
* @return string The renderer view
|
* @return string The rendered view
|
||||||
*/
|
*/
|
||||||
public function renderView($view, array $parameters = array())
|
public function renderView($view, array $parameters = array())
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,11 @@ class ControllerNameParser
|
||||||
* Converts a short notation a:b:c to a class::method.
|
* Converts a short notation a:b:c to a class::method.
|
||||||
*
|
*
|
||||||
* @param string $controller A short notation controller (a:b:c)
|
* @param string $controller A short notation controller (a:b:c)
|
||||||
|
*
|
||||||
|
* @return string A string with class::method
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException when the specified bundle is not enabled
|
||||||
|
* or the controller cannot be found
|
||||||
*/
|
*/
|
||||||
public function parse($controller)
|
public function parse($controller)
|
||||||
{
|
{
|
||||||
|
@ -47,40 +52,23 @@ class ControllerNameParser
|
||||||
|
|
||||||
list($bundle, $controller, $action) = $parts;
|
list($bundle, $controller, $action) = $parts;
|
||||||
$controller = str_replace('/', '\\', $controller);
|
$controller = str_replace('/', '\\', $controller);
|
||||||
$class = null;
|
$bundles = array();
|
||||||
$logs = array();
|
|
||||||
|
// this throws an exception if there is no such bundle
|
||||||
foreach ($this->kernel->getBundle($bundle, false) as $b) {
|
foreach ($this->kernel->getBundle($bundle, false) as $b) {
|
||||||
$try = $b->getNamespace().'\\Controller\\'.$controller.'Controller';
|
$try = $b->getNamespace().'\\Controller\\'.$controller.'Controller';
|
||||||
if (!class_exists($try)) {
|
if (class_exists($try)) {
|
||||||
$logs[] = sprintf('Unable to find controller "%s:%s" - class "%s" does not exist.', $bundle, $controller, $try);
|
return $try.'::'.$action.'Action';
|
||||||
} else {
|
|
||||||
$class = $try;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $class) {
|
$bundles[] = $b->getName();
|
||||||
$this->handleControllerNotFoundException($bundle, $controller, $logs);
|
$msg = sprintf('Unable to find controller "%s:%s" - class "%s" does not exist.', $bundle, $controller, $try);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $class.'::'.$action.'Action';
|
if (count($bundles) > 1) {
|
||||||
|
$msg = sprintf('Unable to find controller "%s:%s" in bundles %s.', $bundle, $controller, implode(', ', $bundles));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleControllerNotFoundException($bundle, $controller, array $logs)
|
|
||||||
{
|
|
||||||
// just one log, return it as the exception
|
|
||||||
if (1 == count($logs)) {
|
|
||||||
throw new \InvalidArgumentException($logs[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// many logs, use a message that mentions each searched bundle
|
|
||||||
$names = array();
|
|
||||||
foreach ($this->kernel->getBundle($bundle, false) as $b) {
|
|
||||||
$names[] = $b->getName();
|
|
||||||
}
|
|
||||||
$msg = sprintf('Unable to find controller "%s:%s" in bundles %s.', $bundle, $controller, implode(', ', $names));
|
|
||||||
|
|
||||||
throw new \InvalidArgumentException($msg);
|
throw new \InvalidArgumentException($msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,31 @@ class TemplateController extends ContainerAware
|
||||||
* Renders a template.
|
* Renders a template.
|
||||||
*
|
*
|
||||||
* @param string $template The template name
|
* @param string $template The template name
|
||||||
|
* @param int|null $maxAge Max age for client caching
|
||||||
|
* @param int|null $sharedAge Max age for shared (proxy) caching
|
||||||
|
* @param Boolean|null $private Whether or not caching should apply for client caches only
|
||||||
*
|
*
|
||||||
* @return Response A Response instance
|
* @return Response A Response instance
|
||||||
*/
|
*/
|
||||||
public function templateAction($template)
|
public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null)
|
||||||
{
|
{
|
||||||
return $this->container->get('templating')->renderResponse($template);
|
/** @var $response \Symfony\Component\HttpFoundation\Response */
|
||||||
|
$response = $this->container->get('templating')->renderResponse($template);
|
||||||
|
|
||||||
|
if ($maxAge) {
|
||||||
|
$response->setMaxAge($maxAge);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sharedAge) {
|
||||||
|
$response->setSharedMaxAge($sharedAge);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($private) {
|
||||||
|
$response->setPrivate();
|
||||||
|
} elseif ($private === false || (null === $private && ($maxAge || $sharedAge))) {
|
||||||
|
$response->setPublic($private);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,22 +22,12 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
*/
|
*/
|
||||||
class Configuration implements ConfigurationInterface
|
class Configuration implements ConfigurationInterface
|
||||||
{
|
{
|
||||||
private $debug;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param Boolean $debug Whether to use the debug mode
|
|
||||||
*/
|
|
||||||
public function __construct($debug)
|
|
||||||
{
|
|
||||||
$this->debug = (Boolean) $debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the configuration tree builder.
|
* Generates the configuration tree builder.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
|
* @return TreeBuilder The tree builder
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException When using the deprecated 'charset' setting
|
||||||
*/
|
*/
|
||||||
public function getConfigTreeBuilder()
|
public function getConfigTreeBuilder()
|
||||||
{
|
{
|
||||||
|
@ -179,7 +169,7 @@ class Configuration implements ConfigurationInterface
|
||||||
->children()
|
->children()
|
||||||
->booleanNode('auto_start')
|
->booleanNode('auto_start')
|
||||||
->info('DEPRECATED! Session starts on demand')
|
->info('DEPRECATED! Session starts on demand')
|
||||||
->defaultNull()
|
->defaultFalse()
|
||||||
->beforeNormalization()
|
->beforeNormalization()
|
||||||
->ifTrue(function($v) { return null !== $v; })
|
->ifTrue(function($v) { return null !== $v; })
|
||||||
->then(function($v) {
|
->then(function($v) {
|
||||||
|
@ -384,7 +374,7 @@ class Configuration implements ConfigurationInterface
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('cache')->defaultValue('file')->end()
|
->scalarNode('cache')->defaultValue('file')->end()
|
||||||
->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end()
|
->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end()
|
||||||
->booleanNode('debug')->defaultValue($this->debug)->end()
|
->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
|
|
@ -141,11 +141,6 @@ class FrameworkExtension extends Extension
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
|
||||||
{
|
|
||||||
return new Configuration($container->getParameter('kernel.debug'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads Form configuration.
|
* Loads Form configuration.
|
||||||
*
|
*
|
||||||
|
|
|
@ -105,6 +105,7 @@ class HttpKernel extends BaseHttpKernel
|
||||||
'alt' => array(),
|
'alt' => array(),
|
||||||
'standalone' => false,
|
'standalone' => false,
|
||||||
'comment' => '',
|
'comment' => '',
|
||||||
|
'default' => null,
|
||||||
), $options);
|
), $options);
|
||||||
|
|
||||||
if (!is_array($options['alt'])) {
|
if (!is_array($options['alt'])) {
|
||||||
|
@ -130,8 +131,16 @@ class HttpKernel extends BaseHttpKernel
|
||||||
$uri = $this->generateInternalUri($controller, $options['attributes'], $options['query'], false);
|
$uri = $this->generateInternalUri($controller, $options['attributes'], $options['query'], false);
|
||||||
$defaultContent = null;
|
$defaultContent = null;
|
||||||
|
|
||||||
if ($template = $this->container->getParameter('templating.hinclude.default_template')) {
|
$templating = $this->container->get('templating');
|
||||||
$defaultContent = $this->container->get('templating')->render($template);
|
|
||||||
|
if ($options['default']) {
|
||||||
|
if ($templating->exists($options['default'])) {
|
||||||
|
$defaultContent = $templating->render($options['default']);
|
||||||
|
} else {
|
||||||
|
$defaultContent = $options['default'];
|
||||||
|
}
|
||||||
|
} elseif ($template = $this->container->getParameter('templating.hinclude.default_template')) {
|
||||||
|
$defaultContent = $templating->render($template);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->renderHIncludeTag($uri, $defaultContent);
|
return $this->renderHIncludeTag($uri, $defaultContent);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<parameter key="debug.stopwatch.class">Symfony\Component\Stopwatch\Stopwatch</parameter>
|
<parameter key="debug.stopwatch.class">Symfony\Component\Stopwatch\Stopwatch</parameter>
|
||||||
<parameter key="debug.container.dump">%kernel.cache_dir%/%kernel.container_class%.xml</parameter>
|
<parameter key="debug.container.dump">%kernel.cache_dir%/%kernel.container_class%.xml</parameter>
|
||||||
<parameter key="debug.controller_resolver.class">Symfony\Component\HttpKernel\Controller\TraceableControllerResolver</parameter>
|
<parameter key="debug.controller_resolver.class">Symfony\Component\HttpKernel\Controller\TraceableControllerResolver</parameter>
|
||||||
|
<parameter key="debug.deprecation_logger_listener.class">Symfony\Component\HttpKernel\EventListener\DeprecationLoggerListener</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
@ -26,5 +27,11 @@
|
||||||
<argument type="service" id="controller_resolver" />
|
<argument type="service" id="controller_resolver" />
|
||||||
<argument type="service" id="debug.stopwatch" />
|
<argument type="service" id="debug.stopwatch" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="debug.deprecation_logger_listener" class="%debug.deprecation_logger_listener.class%">
|
||||||
|
<tag name="kernel.event_subscriber" />
|
||||||
|
<tag name="monolog.logger" channel="deprecation" />
|
||||||
|
<argument type="service" id="logger" on-invalid="null" />
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<parameter key="translation.loader.xliff.class">Symfony\Component\Translation\Loader\XliffFileLoader</parameter>
|
<parameter key="translation.loader.xliff.class">Symfony\Component\Translation\Loader\XliffFileLoader</parameter>
|
||||||
<parameter key="translation.loader.po.class">Symfony\Component\Translation\Loader\PoFileLoader</parameter>
|
<parameter key="translation.loader.po.class">Symfony\Component\Translation\Loader\PoFileLoader</parameter>
|
||||||
<parameter key="translation.loader.mo.class">Symfony\Component\Translation\Loader\MoFileLoader</parameter>
|
<parameter key="translation.loader.mo.class">Symfony\Component\Translation\Loader\MoFileLoader</parameter>
|
||||||
<parameter key="translation.loader.qt.class">Symfony\Component\Translation\Loader\QtTranslationsLoader</parameter>
|
<parameter key="translation.loader.qt.class">Symfony\Component\Translation\Loader\QtFileLoader</parameter>
|
||||||
<parameter key="translation.loader.csv.class">Symfony\Component\Translation\Loader\CsvFileLoader</parameter>
|
<parameter key="translation.loader.csv.class">Symfony\Component\Translation\Loader\CsvFileLoader</parameter>
|
||||||
<parameter key="translation.loader.res.class">Symfony\Component\Translation\Loader\IcuResFileLoader</parameter>
|
<parameter key="translation.loader.res.class">Symfony\Component\Translation\Loader\IcuResFileLoader</parameter>
|
||||||
<parameter key="translation.loader.dat.class">Symfony\Component\Translation\Loader\IcuDatFileLoader</parameter>
|
<parameter key="translation.loader.dat.class">Symfony\Component\Translation\Loader\IcuDatFileLoader</parameter>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
<?php if (false !== $label): ?>
|
||||||
<?php if ($required) { $label_attr['class'] = trim((isset($label_attr['class']) ? $label_attr['class'] : '').' required'); } ?>
|
<?php if ($required) { $label_attr['class'] = trim((isset($label_attr['class']) ? $label_attr['class'] : '').' required'); } ?>
|
||||||
<?php if (!$compound) { $label_attr['for'] = $id; } ?>
|
<?php if (!$compound) { $label_attr['for'] = $id; } ?>
|
||||||
<?php if (!$label) { $label = $view['form']->humanize($name); } ?>
|
<?php if (!$label) { $label = $view['form']->humanize($name); } ?>
|
||||||
<label <?php foreach ($label_attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape($view['translator']->trans($label, array(), $translation_domain)) ?></label>
|
<label <?php foreach ($label_attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape($view['translator']->trans($label, array(), $translation_domain)) ?></label>
|
||||||
|
<?php endif ?>
|
||||||
|
|
|
@ -85,9 +85,6 @@ class Router extends BaseRouter implements WarmableInterface
|
||||||
private function resolveParameters(RouteCollection $collection)
|
private function resolveParameters(RouteCollection $collection)
|
||||||
{
|
{
|
||||||
foreach ($collection as $route) {
|
foreach ($collection as $route) {
|
||||||
if ($route instanceof RouteCollection) {
|
|
||||||
$this->resolveParameters($route);
|
|
||||||
} else {
|
|
||||||
foreach ($route->getDefaults() as $name => $value) {
|
foreach ($route->getDefaults() as $name => $value) {
|
||||||
$route->setDefault($name, $this->resolve($value));
|
$route->setDefault($name, $this->resolve($value));
|
||||||
}
|
}
|
||||||
|
@ -100,7 +97,6 @@ class Router extends BaseRouter implements WarmableInterface
|
||||||
$route->setHostnamePattern($this->resolve($route->getHostnamePattern()));
|
$route->setHostnamePattern($this->resolve($route->getHostnamePattern()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively replaces placeholders with the service container parameters.
|
* Recursively replaces placeholders with the service container parameters.
|
||||||
|
|
|
@ -43,6 +43,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface
|
||||||
'failure_path' => null,
|
'failure_path' => null,
|
||||||
'failure_forward' => false,
|
'failure_forward' => false,
|
||||||
'login_path' => '/login',
|
'login_path' => '/login',
|
||||||
|
'failure_path_parameter' => '_failure_path',
|
||||||
);
|
);
|
||||||
|
|
||||||
public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId)
|
public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId)
|
||||||
|
|
|
@ -33,6 +33,7 @@ security:
|
||||||
check_path: /login_check
|
check_path: /login_check
|
||||||
default_target_path: /profile
|
default_target_path: /profile
|
||||||
target_path_parameter: "user_login[_target_path]"
|
target_path_parameter: "user_login[_target_path]"
|
||||||
|
failure_path_parameter: "user_login[_failure_path]"
|
||||||
username_parameter: "user_login[username]"
|
username_parameter: "user_login[username]"
|
||||||
password_parameter: "user_login[password]"
|
password_parameter: "user_login[password]"
|
||||||
csrf_parameter: "user_login[_token]"
|
csrf_parameter: "user_login[_token]"
|
||||||
|
|
|
@ -4,6 +4,8 @@ CHANGELOG
|
||||||
2.2.0
|
2.2.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* moved the exception controller to be a service (`twig.controller.exception:showAction` vs `Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController::showAction`)
|
||||||
|
* added support for multiple loaders via the "twig.loader" tag.
|
||||||
* added automatic registration of namespaced paths for registered bundles
|
* added automatic registration of namespaced paths for registered bundles
|
||||||
* added support for namespaced paths
|
* added support for namespaced paths
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ namespace Symfony\Bundle\TwigBundle\Controller;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
|
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
|
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
|
||||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||||
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,8 +23,17 @@ use Symfony\Component\HttpFoundation\Response;
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
class ExceptionController extends ContainerAware
|
class ExceptionController
|
||||||
{
|
{
|
||||||
|
protected $twig;
|
||||||
|
protected $debug;
|
||||||
|
|
||||||
|
public function __construct(\Twig_Environment $twig, $debug)
|
||||||
|
{
|
||||||
|
$this->twig = $twig;
|
||||||
|
$this->debug = $debug;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an Exception to a Response.
|
* Converts an Exception to a Response.
|
||||||
*
|
*
|
||||||
|
@ -36,17 +45,16 @@ class ExceptionController extends ContainerAware
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When the exception template does not exist
|
* @throws \InvalidArgumentException When the exception template does not exist
|
||||||
*/
|
*/
|
||||||
public function showAction(FlattenException $exception, DebugLoggerInterface $logger = null, $format = 'html')
|
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null, $format = 'html')
|
||||||
{
|
{
|
||||||
$this->container->get('request')->setRequestFormat($format);
|
$request->setRequestFormat($format);
|
||||||
|
|
||||||
$currentContent = $this->getAndCleanOutputBuffering();
|
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
|
||||||
|
|
||||||
$templating = $this->container->get('templating');
|
|
||||||
$code = $exception->getStatusCode();
|
$code = $exception->getStatusCode();
|
||||||
|
|
||||||
return $templating->renderResponse(
|
return new Response($this->twig->render(
|
||||||
$this->findTemplate($templating, $format, $code, $this->container->get('kernel')->isDebug()),
|
$this->findTemplate($request, $format, $code, $this->debug),
|
||||||
array(
|
array(
|
||||||
'status_code' => $code,
|
'status_code' => $code,
|
||||||
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
|
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
|
||||||
|
@ -54,19 +62,17 @@ class ExceptionController extends ContainerAware
|
||||||
'logger' => $logger,
|
'logger' => $logger,
|
||||||
'currentContent' => $currentContent,
|
'currentContent' => $currentContent,
|
||||||
)
|
)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getAndCleanOutputBuffering()
|
protected function getAndCleanOutputBuffering($startObLevel)
|
||||||
{
|
{
|
||||||
// ob_get_level() never returns 0 on some Windows configurations, so if
|
// ob_get_level() never returns 0 on some Windows configurations, so if
|
||||||
// the level is the same two times in a row, the loop should be stopped.
|
// the level is the same two times in a row, the loop should be stopped.
|
||||||
$previousObLevel = null;
|
$previousObLevel = null;
|
||||||
$startObLevel = $this->container->get('request')->headers->get('X-Php-Ob-Level', -1);
|
|
||||||
|
|
||||||
$currentContent = '';
|
$currentContent = '';
|
||||||
|
|
||||||
while (($obLevel = ob_get_level()) > $startObLevel && $obLevel !== $previousObLevel) {
|
while (($obLevel = ob_get_level()) > $startObLevel && $obLevel !== $previousObLevel) {
|
||||||
|
@ -78,14 +84,14 @@ class ExceptionController extends ContainerAware
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param EngineInterface $templating
|
* @param Request $request
|
||||||
* @param string $format
|
* @param string $format
|
||||||
* @param integer $code An HTTP response status code
|
* @param integer $code An HTTP response status code
|
||||||
* @param Boolean $debug
|
* @param Boolean $debug
|
||||||
*
|
*
|
||||||
* @return TemplateReference
|
* @return TemplateReference
|
||||||
*/
|
*/
|
||||||
protected function findTemplate($templating, $format, $code, $debug)
|
protected function findTemplate(Request $request, $format, $code, $debug)
|
||||||
{
|
{
|
||||||
$name = $debug ? 'exception' : 'error';
|
$name = $debug ? 'exception' : 'error';
|
||||||
if ($debug && 'html' == $format) {
|
if ($debug && 'html' == $format) {
|
||||||
|
@ -95,20 +101,38 @@ class ExceptionController extends ContainerAware
|
||||||
// when not in debug, try to find a template for the specific HTTP status code and format
|
// when not in debug, try to find a template for the specific HTTP status code and format
|
||||||
if (!$debug) {
|
if (!$debug) {
|
||||||
$template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig');
|
$template = new TemplateReference('TwigBundle', 'Exception', $name.$code, $format, 'twig');
|
||||||
if ($templating->exists($template)) {
|
if ($this->templateExists($template)) {
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to find a template for the given format
|
// try to find a template for the given format
|
||||||
$template = new TemplateReference('TwigBundle', 'Exception', $name, $format, 'twig');
|
$template = new TemplateReference('TwigBundle', 'Exception', $name, $format, 'twig');
|
||||||
if ($templating->exists($template)) {
|
if ($this->templateExists($template)) {
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
// default to a generic HTML exception
|
// default to a generic HTML exception
|
||||||
$this->container->get('request')->setRequestFormat('html');
|
$request->setRequestFormat('html');
|
||||||
|
|
||||||
return new TemplateReference('TwigBundle', 'Exception', $name, 'html', 'twig');
|
return new TemplateReference('TwigBundle', 'Exception', $name, 'html', 'twig');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to be removed when the minimum required version of Twig is >= 2.0
|
||||||
|
protected function templateExists($template)
|
||||||
|
{
|
||||||
|
$loader = $this->twig->getLoader();
|
||||||
|
if ($loader instanceof \Twig_ExistsLoaderInterface) {
|
||||||
|
return $loader->exists($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$loader->getSource($template);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Twig_Error_Loader $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds services tagged twig.loader as Twig loaders
|
||||||
|
*
|
||||||
|
* @author Daniel Leech <daniel@dantleech.com>
|
||||||
|
*/
|
||||||
|
class TwigLoaderPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
if (false === $container->hasDefinition('twig')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// register additional template loaders
|
||||||
|
$loaderIds = $container->findTaggedServiceIds('twig.loader');
|
||||||
|
|
||||||
|
if (count($loaderIds) === 0) {
|
||||||
|
throw new LogicException('No twig loaders found. You need to tag at least one loader with "twig.loader"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($loaderIds) === 1) {
|
||||||
|
$container->setAlias('twig.loader', key($loaderIds));
|
||||||
|
} else {
|
||||||
|
$chainLoader = $container->getDefinition('twig.loader.chain');
|
||||||
|
foreach (array_keys($loaderIds) as $id) {
|
||||||
|
$chainLoader->addMethodCall('addLoader', array(new Reference($id)));
|
||||||
|
}
|
||||||
|
$container->setAlias('twig.loader', 'twig.loader.chain');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ class Configuration implements ConfigurationInterface
|
||||||
|
|
||||||
$rootNode
|
$rootNode
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('exception_controller')->defaultValue('Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController::showAction')->end()
|
->scalarNode('exception_controller')->defaultValue('twig.controller.exception:showAction')->end()
|
||||||
->end()
|
->end()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,13 @@
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="twig.class">Twig_Environment</parameter>
|
<parameter key="twig.class">Twig_Environment</parameter>
|
||||||
<parameter key="twig.loader.filesystem.class">Symfony\Bundle\TwigBundle\Loader\FilesystemLoader</parameter>
|
<parameter key="twig.loader.filesystem.class">Symfony\Bundle\TwigBundle\Loader\FilesystemLoader</parameter>
|
||||||
|
<parameter key="twig.loader.chain.class">Twig_Loader_Chain</parameter>
|
||||||
<parameter key="templating.engine.twig.class">Symfony\Bundle\TwigBundle\TwigEngine</parameter>
|
<parameter key="templating.engine.twig.class">Symfony\Bundle\TwigBundle\TwigEngine</parameter>
|
||||||
<parameter key="twig.cache_warmer.class">Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer</parameter>
|
<parameter key="twig.cache_warmer.class">Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer</parameter>
|
||||||
<parameter key="twig.extension.trans.class">Symfony\Bridge\Twig\Extension\TranslationExtension</parameter>
|
<parameter key="twig.extension.trans.class">Symfony\Bridge\Twig\Extension\TranslationExtension</parameter>
|
||||||
<parameter key="twig.extension.assets.class">Symfony\Bundle\TwigBundle\Extension\AssetsExtension</parameter>
|
<parameter key="twig.extension.assets.class">Symfony\Bundle\TwigBundle\Extension\AssetsExtension</parameter>
|
||||||
<parameter key="twig.extension.actions.class">Symfony\Bundle\TwigBundle\Extension\ActionsExtension</parameter>
|
<parameter key="twig.extension.actions.class">Symfony\Bundle\TwigBundle\Extension\ActionsExtension</parameter>
|
||||||
<parameter key="twig.extension.code.class">Symfony\Bundle\TwigBundle\Extension\CodeExtension</parameter>
|
<parameter key="twig.extension.code.class">Symfony\Bridge\Twig\Extension\CodeExtension</parameter>
|
||||||
<parameter key="twig.extension.routing.class">Symfony\Bridge\Twig\Extension\RoutingExtension</parameter>
|
<parameter key="twig.extension.routing.class">Symfony\Bridge\Twig\Extension\RoutingExtension</parameter>
|
||||||
<parameter key="twig.extension.yaml.class">Symfony\Bridge\Twig\Extension\YamlExtension</parameter>
|
<parameter key="twig.extension.yaml.class">Symfony\Bridge\Twig\Extension\YamlExtension</parameter>
|
||||||
<parameter key="twig.extension.form.class">Symfony\Bridge\Twig\Extension\FormExtension</parameter>
|
<parameter key="twig.extension.form.class">Symfony\Bridge\Twig\Extension\FormExtension</parameter>
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
<parameter key="twig.form.renderer.class">Symfony\Bridge\Twig\Form\TwigRenderer</parameter>
|
<parameter key="twig.form.renderer.class">Symfony\Bridge\Twig\Form\TwigRenderer</parameter>
|
||||||
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
|
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
|
||||||
<parameter key="twig.exception_listener.class">Symfony\Component\HttpKernel\EventListener\ExceptionListener</parameter>
|
<parameter key="twig.exception_listener.class">Symfony\Component\HttpKernel\EventListener\ExceptionListener</parameter>
|
||||||
|
<parameter key="twig.controller.exception.class">Symfony\Bundle\TwigBundle\Controller\ExceptionController</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
@ -41,8 +43,11 @@
|
||||||
<service id="twig.loader.filesystem" class="%twig.loader.filesystem.class%" public="false">
|
<service id="twig.loader.filesystem" class="%twig.loader.filesystem.class%" public="false">
|
||||||
<argument type="service" id="templating.locator" />
|
<argument type="service" id="templating.locator" />
|
||||||
<argument type="service" id="templating.name_parser" />
|
<argument type="service" id="templating.name_parser" />
|
||||||
|
<tag name="twig.loader"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="twig.loader.chain" class="%twig.loader.chain.class%" public="false"/>
|
||||||
|
|
||||||
<service id="twig.loader" alias="twig.loader.filesystem" />
|
<service id="twig.loader" alias="twig.loader.filesystem" />
|
||||||
|
|
||||||
<service id="templating.engine.twig" class="%templating.engine.twig.class%" public="false">
|
<service id="templating.engine.twig" class="%templating.engine.twig.class%" public="false">
|
||||||
|
@ -107,5 +112,10 @@
|
||||||
<argument>%twig.exception_listener.controller%</argument>
|
<argument>%twig.exception_listener.controller%</argument>
|
||||||
<argument type="service" id="logger" on-invalid="null" />
|
<argument type="service" id="logger" on-invalid="null" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="twig.controller.exception" class="%twig.controller.exception.class%">
|
||||||
|
<argument type="service" id="twig" />
|
||||||
|
<argument>%kernel.debug%</argument>
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
|
|
@ -12,72 +12,33 @@
|
||||||
namespace Symfony\Bundle\TwigBundle\Tests\Controller;
|
namespace Symfony\Bundle\TwigBundle\Tests\Controller;
|
||||||
|
|
||||||
use Symfony\Bundle\TwigBundle\Tests\TestCase;
|
use Symfony\Bundle\TwigBundle\Tests\TestCase;
|
||||||
|
|
||||||
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
|
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|
||||||
use Symfony\Component\DependencyInjection\Scope;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
class ExceptionControllerTest extends TestCase
|
class ExceptionControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
protected $controller;
|
public function testOnlyClearOwnOutputBuffers()
|
||||||
protected $container;
|
|
||||||
protected $flatten;
|
|
||||||
protected $templating;
|
|
||||||
protected $kernel;
|
|
||||||
|
|
||||||
protected function setUp()
|
|
||||||
{
|
{
|
||||||
parent::setUp();
|
$flatten = $this->getMock('Symfony\Component\HttpKernel\Exception\FlattenException');
|
||||||
|
$flatten
|
||||||
$this->flatten = $this->getMock('Symfony\Component\HttpKernel\Exception\FlattenException');
|
|
||||||
$this->flatten
|
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getStatusCode')
|
->method('getStatusCode')
|
||||||
->will($this->returnValue(404));
|
->will($this->returnValue(404));
|
||||||
$this->controller = new ExceptionController();
|
$twig = $this->getMockBuilder('\Twig_Environment')
|
||||||
$this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface');
|
|
||||||
$this->templating = $this->getMockBuilder('Symfony\\Bundle\\TwigBundle\\TwigEngine')
|
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
$this->templating
|
$twig
|
||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
->method('renderResponse')
|
->method('render')
|
||||||
->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Response')));
|
->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Response')));
|
||||||
$this->request = Request::create('/');
|
$twig
|
||||||
$this->container = $this->getContainer();
|
->expects($this->any())
|
||||||
}
|
->method('getLoader')
|
||||||
|
->will($this->returnValue($this->getMock('\Twig_LoaderInterface')));
|
||||||
|
$request = Request::create('/');
|
||||||
|
$request->headers->set('X-Php-Ob-Level', 1);
|
||||||
|
|
||||||
protected function tearDown()
|
$controller = new ExceptionController($twig, false);
|
||||||
{
|
$controller->showAction($request, $flatten);
|
||||||
parent::tearDown();
|
|
||||||
|
|
||||||
$this->controller = null;
|
|
||||||
$this->container = null;
|
|
||||||
$this->flatten = null;
|
|
||||||
$this->templating = null;
|
|
||||||
$this->kernel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testOnlyClearOwnOutputBuffers()
|
|
||||||
{
|
|
||||||
$this->request->headers->set('X-Php-Ob-Level', 1);
|
|
||||||
|
|
||||||
$this->controller->setContainer($this->container);
|
|
||||||
$this->controller->showAction($this->flatten);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getContainer()
|
|
||||||
{
|
|
||||||
$container = new ContainerBuilder();
|
|
||||||
$container->addScope(new Scope('request'));
|
|
||||||
$container->set('request', $this->request);
|
|
||||||
$container->set('templating', $this->templating);
|
|
||||||
$container->setParameter('kernel.bundles', array());
|
|
||||||
$container->setParameter('kernel.cache_dir', __DIR__);
|
|
||||||
$container->setParameter('kernel.root_dir', __DIR__);
|
|
||||||
$container->set('kernel', $this->kernel);
|
|
||||||
|
|
||||||
return $container;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace symfony\src\Symfony\Bundle\TwigBundle\Tests\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
|
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass;
|
||||||
|
|
||||||
|
class TwigLoaderPassTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||||
|
$this->chainLoader = new Definition('loader');
|
||||||
|
$this->pass = new TwigLoaderPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapperPassWithOneTaggedLoaders()
|
||||||
|
{
|
||||||
|
$serviceIds = array(
|
||||||
|
'test_loader_1' => array(
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('hasDefinition')
|
||||||
|
->with('twig')
|
||||||
|
->will($this->returnValue(true));
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('findTaggedServiceIds')
|
||||||
|
->with('twig.loader')
|
||||||
|
->will($this->returnValue($serviceIds));
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('setAlias')
|
||||||
|
->with('twig.loader', 'test_loader_1');
|
||||||
|
|
||||||
|
$this->pass->process($this->builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapperPassWithTwoTaggedLoaders()
|
||||||
|
{
|
||||||
|
$serviceIds = array(
|
||||||
|
'test_loader_1' => array(
|
||||||
|
),
|
||||||
|
'test_loader_2' => array(
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('hasDefinition')
|
||||||
|
->with('twig')
|
||||||
|
->will($this->returnValue(true));
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('findTaggedServiceIds')
|
||||||
|
->with('twig.loader')
|
||||||
|
->will($this->returnValue($serviceIds));
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('getDefinition')
|
||||||
|
->with('twig.loader.chain')
|
||||||
|
->will($this->returnValue($this->chainLoader));
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('setAlias')
|
||||||
|
->with('twig.loader', 'twig.loader.chain');
|
||||||
|
|
||||||
|
$this->pass->process($this->builder);
|
||||||
|
$calls = $this->chainLoader->getMethodCalls();
|
||||||
|
$this->assertEquals(2, count($calls));
|
||||||
|
$this->assertEquals('addLoader', $calls[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\DependencyInjection\Exception\LogicException
|
||||||
|
*/
|
||||||
|
public function testMapperPassWithZeroTaggedLoaders()
|
||||||
|
{
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('hasDefinition')
|
||||||
|
->with('twig')
|
||||||
|
->will($this->returnValue(true));
|
||||||
|
$this->builder->expects($this->once())
|
||||||
|
->method('findTaggedServiceIds')
|
||||||
|
->with('twig.loader')
|
||||||
|
->will($this->returnValue(array()));
|
||||||
|
|
||||||
|
$this->pass->process($this->builder);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,10 +60,9 @@ class TwigExtensionTest extends TestCase
|
||||||
// Globals
|
// Globals
|
||||||
$calls = $container->getDefinition('twig')->getMethodCalls();
|
$calls = $container->getDefinition('twig')->getMethodCalls();
|
||||||
$this->assertEquals('app', $calls[0][1][0], '->load() registers services as Twig globals');
|
$this->assertEquals('app', $calls[0][1][0], '->load() registers services as Twig globals');
|
||||||
|
$this->assertEquals(new Reference('templating.globals'), $calls[0][1][1]);
|
||||||
$this->assertEquals('foo', $calls[1][1][0], '->load() registers services as Twig globals');
|
$this->assertEquals('foo', $calls[1][1][0], '->load() registers services as Twig globals');
|
||||||
$this->assertEquals(new Reference('bar'), $calls[1][1][1], '->load() registers services as Twig globals');
|
$this->assertEquals(new Reference('bar'), $calls[1][1][1], '->load() registers services as Twig globals');
|
||||||
|
|
||||||
$this->assertEquals('pi', $calls[2][1][0], '->load() registers variables as Twig globals');
|
$this->assertEquals('pi', $calls[2][1][0], '->load() registers variables as Twig globals');
|
||||||
$this->assertEquals(3.14, $calls[2][1][1], '->load() registers variables as Twig globals');
|
$this->assertEquals(3.14, $calls[2][1][1], '->load() registers variables as Twig globals');
|
||||||
|
|
||||||
|
@ -103,8 +102,7 @@ class TwigExtensionTest extends TestCase
|
||||||
$this->compileContainer($container);
|
$this->compileContainer($container);
|
||||||
|
|
||||||
$calls = $container->getDefinition('twig')->getMethodCalls();
|
$calls = $container->getDefinition('twig')->getMethodCalls();
|
||||||
array_shift($calls);
|
foreach (array_slice($calls, 1) as $call) {
|
||||||
foreach ($calls as $call) {
|
|
||||||
list($name, $value) = each($globals);
|
list($name, $value) = each($globals);
|
||||||
$this->assertEquals($name, $call[1][0]);
|
$this->assertEquals($name, $call[1][0]);
|
||||||
$this->assertSame($value, $call[1][1]);
|
$this->assertSame($value, $call[1][1]);
|
||||||
|
@ -137,7 +135,7 @@ class TwigExtensionTest extends TestCase
|
||||||
array('namespaced_path1', 'namespace'),
|
array('namespaced_path1', 'namespace'),
|
||||||
array('namespaced_path2', 'namespace'),
|
array('namespaced_path2', 'namespace'),
|
||||||
array(__DIR__.'/Fixtures/Resources/TwigBundle/views', 'Twig'),
|
array(__DIR__.'/Fixtures/Resources/TwigBundle/views', 'Twig'),
|
||||||
array(realpath(__DIR__.'/../../Resources/views'), 'Twig'),
|
array(realpath(__DIR__.'/../..').'/Resources/views', 'Twig'),
|
||||||
array(__DIR__.'/Fixtures/Resources/views'),
|
array(__DIR__.'/Fixtures/Resources/views'),
|
||||||
), $paths);
|
), $paths);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of the Symfony package.
|
|
||||||
*
|
|
||||||
* (c) Fabien Potencier <fabien@symfony.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Symfony\Bundle\TwigBundle\Tests;
|
|
||||||
|
|
||||||
use Symfony\Bundle\TwigBundle\TwigEngine;
|
|
||||||
use Symfony\Component\DependencyInjection\Container;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
|
||||||
use Symfony\Component\Templating\TemplateNameParser;
|
|
||||||
|
|
||||||
class TwigEngineTest extends TestCase
|
|
||||||
{
|
|
||||||
public function testEvaluateAddsAppGlobal()
|
|
||||||
{
|
|
||||||
$environment = $this->getTwigEnvironment();
|
|
||||||
$container = $this->getContainer();
|
|
||||||
$locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface');
|
|
||||||
$engine = new TwigEngine($environment, new TemplateNameParser(), $locator);
|
|
||||||
|
|
||||||
$template = $this->getMock('\Twig_TemplateInterface');
|
|
||||||
|
|
||||||
$environment->expects($this->once())
|
|
||||||
->method('loadTemplate')
|
|
||||||
->will($this->returnValue($template));
|
|
||||||
|
|
||||||
$engine->render('name');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testEvaluateWithoutAvailableRequest()
|
|
||||||
{
|
|
||||||
$environment = $this->getTwigEnvironment();
|
|
||||||
$container = new Container();
|
|
||||||
$locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface');
|
|
||||||
$engine = new TwigEngine($environment, new TemplateNameParser(), $locator);
|
|
||||||
|
|
||||||
$template = $this->getMock('\Twig_TemplateInterface');
|
|
||||||
|
|
||||||
$environment->expects($this->once())
|
|
||||||
->method('loadTemplate')
|
|
||||||
->will($this->returnValue($template));
|
|
||||||
|
|
||||||
$container->set('request', null);
|
|
||||||
|
|
||||||
$engine->render('name');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a Container with a Session-containing Request service.
|
|
||||||
*
|
|
||||||
* @return Container
|
|
||||||
*/
|
|
||||||
protected function getContainer()
|
|
||||||
{
|
|
||||||
$container = new Container();
|
|
||||||
$request = new Request();
|
|
||||||
$session = new Session(new MockArraySessionStorage());
|
|
||||||
|
|
||||||
$request->setSession($session);
|
|
||||||
$container->set('request', $request);
|
|
||||||
|
|
||||||
return $container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a mock Twig_Environment object.
|
|
||||||
*
|
|
||||||
* @return \Twig_Environment
|
|
||||||
*/
|
|
||||||
protected function getTwigEnvironment()
|
|
||||||
{
|
|
||||||
return $this
|
|
||||||
->getMockBuilder('\Twig_Environment')
|
|
||||||
->setMethods(array('loadTemplate'))
|
|
||||||
->getMock();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,6 +14,7 @@ namespace Symfony\Bundle\TwigBundle;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass;
|
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass;
|
||||||
|
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass;
|
||||||
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExceptionListenerPass;
|
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExceptionListenerPass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +29,7 @@ class TwigBundle extends Bundle
|
||||||
parent::build($container);
|
parent::build($container);
|
||||||
|
|
||||||
$container->addCompilerPass(new TwigEnvironmentPass());
|
$container->addCompilerPass(new TwigEnvironmentPass());
|
||||||
|
$container->addCompilerPass(new TwigLoaderPass());
|
||||||
$container->addCompilerPass(new ExceptionListenerPass());
|
$container->addCompilerPass(new ExceptionListenerPass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
vendor/
|
||||||
|
composer.lock
|
||||||
|
phpunit.xml
|
||||||
|
|
|
@ -12,27 +12,52 @@
|
||||||
namespace Symfony\Bundle\WebProfilerBundle\Controller;
|
namespace Symfony\Bundle\WebProfilerBundle\Controller;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||||
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||||
|
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Bundle\TwigBundle\Controller\ExceptionController as BaseExceptionController;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ExceptionController.
|
* ExceptionController.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
class ExceptionController extends BaseExceptionController
|
class ExceptionController
|
||||||
{
|
{
|
||||||
|
protected $twig;
|
||||||
|
protected $debug;
|
||||||
|
protected $profiler;
|
||||||
|
|
||||||
|
public function __construct(Profiler $profiler, \Twig_Environment $twig, $debug)
|
||||||
|
{
|
||||||
|
$this->profiler = $profiler;
|
||||||
|
$this->twig = $twig;
|
||||||
|
$this->debug = $debug;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* Renders the exception panel for the given token.
|
||||||
|
*
|
||||||
|
* @param string $token The profiler token
|
||||||
|
*
|
||||||
|
* @return Response A Response instance
|
||||||
*/
|
*/
|
||||||
public function showAction(FlattenException $exception, DebugLoggerInterface $logger = null, $format = 'html')
|
public function showAction($token)
|
||||||
{
|
{
|
||||||
$template = $this->container->get('kernel')->isDebug() ? 'exception' : 'error';
|
$this->profiler->disable();
|
||||||
|
|
||||||
|
$exception = $this->profiler->loadProfile($token)->getCollector('exception')->getException();
|
||||||
|
$template = $this->getTemplate();
|
||||||
|
|
||||||
|
if (!$this->twig->getLoader()->exists($template)) {
|
||||||
|
$handler = new ExceptionHandler();
|
||||||
|
|
||||||
|
return new Response($handler->getContent($exception));
|
||||||
|
}
|
||||||
|
|
||||||
$code = $exception->getStatusCode();
|
$code = $exception->getStatusCode();
|
||||||
|
|
||||||
return $this->container->get('templating')->renderResponse(
|
return new Response($this->twig->render(
|
||||||
'TwigBundle:Exception:'.$template.'.html.twig',
|
$template,
|
||||||
array(
|
array(
|
||||||
'status_code' => $code,
|
'status_code' => $code,
|
||||||
'status_text' => Response::$statusTexts[$code],
|
'status_text' => Response::$statusTexts[$code],
|
||||||
|
@ -40,6 +65,52 @@ class ExceptionController extends BaseExceptionController
|
||||||
'logger' => null,
|
'logger' => null,
|
||||||
'currentContent' => '',
|
'currentContent' => '',
|
||||||
)
|
)
|
||||||
);
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the exception panel stylesheet for the given token.
|
||||||
|
*
|
||||||
|
* @param string $token The profiler token
|
||||||
|
*
|
||||||
|
* @return Response A Response instance
|
||||||
|
*/
|
||||||
|
public function cssAction($token)
|
||||||
|
{
|
||||||
|
$this->profiler->disable();
|
||||||
|
|
||||||
|
$exception = $this->profiler->loadProfile($token)->getCollector('exception')->getException();
|
||||||
|
$template = $this->getTemplate();
|
||||||
|
|
||||||
|
if (!$this->templateExists($template)) {
|
||||||
|
$handler = new ExceptionHandler();
|
||||||
|
|
||||||
|
return new Response($handler->getStylesheet($exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response($this->twig->render('@WebProfiler/Collector/exception.css.twig'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTemplate()
|
||||||
|
{
|
||||||
|
return '@Twig/Exception/'.($this->debug ? 'exception' : 'error').'.html.twig';
|
||||||
|
}
|
||||||
|
|
||||||
|
// to be removed when the minimum required version of Twig is >= 2.0
|
||||||
|
protected function templateExists($template)
|
||||||
|
{
|
||||||
|
$loader = $this->twig->getLoader();
|
||||||
|
if ($loader instanceof \Twig_ExistsLoaderInterface) {
|
||||||
|
return $loader->exists($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$loader->getSource($template);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Twig_Error_Loader $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,15 +107,31 @@ class TemplateManager
|
||||||
$template = substr($template, 0, -10);
|
$template = substr($template, 0, -10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME when Twig is able to check for template existence
|
if (!$this->templateExists($template.'.html.twig')) {
|
||||||
/*
|
|
||||||
if (!$this->twig->exists($template.'.html.twig')) {
|
|
||||||
throw new \UnexpectedValueException(sprintf('The profiler template "%s.html.twig" for data collector "%s" does not exist.', $template, $name));
|
throw new \UnexpectedValueException(sprintf('The profiler template "%s.html.twig" for data collector "%s" does not exist.', $template, $name));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
$templates[$name] = $template.'.html.twig';
|
$templates[$name] = $template.'.html.twig';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $templates;
|
return $templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to be removed when the minimum required version of Twig is >= 2.0
|
||||||
|
protected function templateExists($template)
|
||||||
|
{
|
||||||
|
$loader = $this->twig->getLoader();
|
||||||
|
if ($loader instanceof \Twig_ExistsLoaderInterface) {
|
||||||
|
return $loader->exists($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$loader->getSource($template);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Twig_Error_Loader $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="web_profiler.controller.profiler.class">Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController</parameter>
|
<parameter key="web_profiler.controller.profiler.class">Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController</parameter>
|
||||||
<parameter key="web_profiler.controller.router.class">Symfony\Bundle\WebProfilerBundle\Controller\RouterController</parameter>
|
<parameter key="web_profiler.controller.router.class">Symfony\Bundle\WebProfilerBundle\Controller\RouterController</parameter>
|
||||||
|
<parameter key="web_profiler.controller.exception.class">Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
@ -23,5 +24,11 @@
|
||||||
<argument type="service" id="twig" />
|
<argument type="service" id="twig" />
|
||||||
<argument type="service" id="router" on-invalid="null" />
|
<argument type="service" id="router" on-invalid="null" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="web_profiler.controller.exception" class="%web_profiler.controller.exception.class%">
|
||||||
|
<argument type="service" id="profiler" />
|
||||||
|
<argument type="service" id="twig" />
|
||||||
|
<argument>%kernel.debug%</argument>
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
{% include '@WebProfiler/Collector/exception.css.twig' %}
|
{% render 'web_profiler.controller.exception:cssAction' with { 'token': token } %}
|
||||||
</style>
|
</style>
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="sf-reset">
|
<div class="sf-reset">
|
||||||
{% render 'WebProfilerBundle:Exception:show' with { 'exception': collector.exception, 'format': 'html' } %}
|
{% render 'web_profiler.controller.exception:showAction' with { 'token': token } %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% import _self as logger %}
|
||||||
|
|
||||||
{% block toolbar %}
|
{% block toolbar %}
|
||||||
{% if collector.counterrors %}
|
{% if collector.counterrors or collector.countdeprecations %}
|
||||||
{% set icon %}
|
{% set icon %}
|
||||||
<img width="15" height="28" alt="Logs" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAcCAYAAABoMT8aAAAA4klEQVQ4y2P4//8/AyWYYXgYwOPp6Xnc3t7+P7EYpB6k7+zZs2ADNEjRjIwDAgKWgAywIUfz8+fPVzg7O/8AGeCATQEQnAfi/SAah/wcV1dXvAYUgORANA75ehcXl+/4DHAABRIe+ZrhbgAhTHsDiEgHBA0glA6GfSDiw5mZma+A+sphBlhVVFQ88vHx+Xfu3Ll7QP5haOjjwtuAuGHv3r3NIMNABqh8+/atsaur666vr+9XUlwSHx//AGQANxCbAnEWyGQicRMQ9wBxIQM0qjiBWAFqkB00/glhayBWHwb1AgB38EJsUtxtWwAAAABJRU5ErkJggg=="/>
|
<img width="15" height="28" alt="Logs" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAcCAYAAABoMT8aAAAA4klEQVQ4y2P4//8/AyWYYXgYwOPp6Xnc3t7+P7EYpB6k7+zZs2ADNEjRjIwDAgKWgAywIUfz8+fPVzg7O/8AGeCATQEQnAfi/SAah/wcV1dXvAYUgORANA75ehcXl+/4DHAABRIe+ZrhbgAhTHsDiEgHBA0glA6GfSDiw5mZma+A+sphBlhVVFQ88vHx+Xfu3Ll7QP5haOjjwtuAuGHv3r3NIMNABqh8+/atsaur666vr+9XUlwSHx//AGQANxCbAnEWyGQicRMQ9wBxIQM0qjiBWAFqkB00/glhayBWHwb1AgB38EJsUtxtWwAAAABJRU5ErkJggg=="/>
|
||||||
<span class="sf-toolbar-status sf-toolbar-status-yellow">{{ collector.counterrors }}</span>
|
{% if collector.counterrors %}
|
||||||
|
{% set status_color = "red" %}
|
||||||
|
{% else %}
|
||||||
|
{% set status_color = "yellow" %}
|
||||||
|
{% endif %}
|
||||||
|
{% set error_count = collector.counterrors + collector.countdeprecations %}
|
||||||
|
<span class="sf-toolbar-status sf-toolbar-status-{{ status_color }}">{{ error_count }}</span>
|
||||||
{% endset %}
|
{% endset %}
|
||||||
{% set text %}
|
{% set text %}
|
||||||
|
{% if collector.counterrors %}
|
||||||
<div class="sf-toolbar-info-piece">
|
<div class="sf-toolbar-info-piece">
|
||||||
<b>Exception</b>
|
<b>Exception</b>
|
||||||
<span class="sf-toolbar-status sf-toolbar-status-yellow">{{ collector.counterrors }}</span>
|
<span class="sf-toolbar-status sf-toolbar-status-red">{{ collector.counterrors }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if collector.countdeprecations %}
|
||||||
|
<div class="sf-toolbar-info-piece">
|
||||||
|
<b>Deprecated Calls</b>
|
||||||
|
<span class="sf-toolbar-status sf-toolbar-status-yellow">{{ collector.countdeprecations }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endset %}
|
{% endset %}
|
||||||
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
|
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -20,9 +36,10 @@
|
||||||
<span class="label">
|
<span class="label">
|
||||||
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAgCAYAAAAMq2gFAAABjElEQVRIx2MIDw+vd3R0/GFvb/+fGtjFxeVJSUmJ1f///5nv37/PAMMMzs7OVLMEhoODgy/k5+cHJCYmagAtZAJbRG1L0DEwxCYALeOgiUXbt2+/X1NT8xTEdnd3/wi0SI4mFgHBDCBeCLXoF5BtwkCEpvNAvB8JnydCTwgQR0It+g1kWxNjUQEQOyDhAiL0gNUiWWRDjEUOyMkUZsCoRaMWjVpEvEVkFkGjFmEUqgc+fvx4hVYWIReqzi9evKileaoDslnu3LkTNLQtGk3edLPIycnpL9Bge5pb1NXVdQNosDmGRcAm7F+QgKur6783b95cBQoeRGv1kII3QPOdAoZF8+fPP4PUqnx55syZVKCEI1rLh1hsAbWEZ8aMGaUoFoFcMG3atKdIjfSPISEhawICAlaQgwMDA1f6+/sfB5rzE2Sej4/PD3C7DkjoAHHVoUOHLpSVlX3w8vL6Sa34Alr6Z8WKFaCoMARZxAHEoFZ/HBD3A/FyIF4BxMvIxCC964F4G6hZDMTxQCwJAGWE8pur5kFDAAAAAElFTkSuQmCC" alt="Logger" /></span>
|
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAgCAYAAAAMq2gFAAABjElEQVRIx2MIDw+vd3R0/GFvb/+fGtjFxeVJSUmJ1f///5nv37/PAMMMzs7OVLMEhoODgy/k5+cHJCYmagAtZAJbRG1L0DEwxCYALeOgiUXbt2+/X1NT8xTEdnd3/wi0SI4mFgHBDCBeCLXoF5BtwkCEpvNAvB8JnydCTwgQR0It+g1kWxNjUQEQOyDhAiL0gNUiWWRDjEUOyMkUZsCoRaMWjVpEvEVkFkGjFmEUqgc+fvx4hVYWIReqzi9evKileaoDslnu3LkTNLQtGk3edLPIycnpL9Bge5pb1NXVdQNosDmGRcAm7F+QgKur6783b95cBQoeRGv1kII3QPOdAoZF8+fPP4PUqnx55syZVKCEI1rLh1hsAbWEZ8aMGaUoFoFcMG3atKdIjfSPISEhawICAlaQgwMDA1f6+/sfB5rzE2Sej4/PD3C7DkjoAHHVoUOHLpSVlX3w8vL6Sa34Alr6Z8WKFaCoMARZxAHEoFZ/HBD3A/FyIF4BxMvIxCC964F4G6hZDMTxQCwJAGWE8pur5kFDAAAAAElFTkSuQmCC" alt="Logger" /></span>
|
||||||
<strong>Logs</strong>
|
<strong>Logs</strong>
|
||||||
{% if collector.counterrors %}
|
{% if collector.counterrors or collector.countdeprecations %}
|
||||||
|
{% set error_count = collector.counterrors + collector.countdeprecations %}
|
||||||
<span class="count">
|
<span class="count">
|
||||||
<span>{{ collector.counterrors }}</span>
|
<span>{{ error_count }}</span>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
|
@ -41,7 +58,8 @@
|
||||||
<input type="hidden" name="panel" value="logger" />
|
<input type="hidden" name="panel" value="logger" />
|
||||||
<label for="priority">Priority</label>
|
<label for="priority">Priority</label>
|
||||||
<select id="priority" name="priority" onchange="document.getElementById('priority-form').submit(); ">
|
<select id="priority" name="priority" onchange="document.getElementById('priority-form').submit(); ">
|
||||||
{% for value, text in { 100: 'DEBUG', 200: 'INFO', 250: 'NOTICE', 300: 'WARNING', 400: 'ERROR', 500: 'CRITICAL', 550: 'ALERT', 600: 'EMERGENCY' } %}
|
{# values < 0 are custom levels #}
|
||||||
|
{% for value, text in { 100: 'DEBUG', 200: 'INFO', 250: 'NOTICE', 300: 'WARNING', 400: 'ERROR', 500: 'CRITICAL', 550: 'ALERT', 600: 'EMERGENCY', '-100': 'DEPRECATION only' } %}
|
||||||
<option value="{{ value }}"{{ value == priority ? ' selected' : '' }}>{{ text }}</option>
|
<option value="{{ value }}"{{ value == priority ? ' selected' : '' }}>{{ text }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
@ -55,15 +73,9 @@
|
||||||
|
|
||||||
{% if collector.logs %}
|
{% if collector.logs %}
|
||||||
<ul class="alt">
|
<ul class="alt">
|
||||||
{% for log in collector.logs if log.priority >= priority %}
|
{% for log in collector.logs if priority >= 0 and log.priority >= priority or priority < 0 and log.context.type|default(0) == priority %}
|
||||||
<li class="{{ cycle(['odd', 'even'], loop.index) }}{% if log.priority >= 400 %} error{% elseif log.priority >= 300 %} warning{% endif %}">
|
<li class="{{ cycle(['odd', 'even'], loop.index) }}{% if log.priority >= 400 %} error{% elseif log.priority >= 300 %} warning{% endif %}">
|
||||||
{{ log.priorityName }} - {{ log.message }}
|
{{ logger.display_message(loop.index, log) }}
|
||||||
{% if log.context is defined and log.context is not empty %}
|
|
||||||
<br />
|
|
||||||
<small>
|
|
||||||
<strong>Context</strong>: {{ log.context|yaml_encode }}
|
|
||||||
</small>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><em>No logs available for this priority.</em></li>
|
<li><em>No logs available for this priority.</em></li>
|
||||||
|
@ -75,3 +87,35 @@
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro display_message(log_index, log) %}
|
||||||
|
{% if constant('Symfony\\Component\\HttpKernel\\Debug\\ErrorHandler::TYPE_DEPRECATION') == log.context.type|default(0) %}
|
||||||
|
DEPRECATION - Deprecated call in {{ log.context.file|format_file(log.context.line) }}.
|
||||||
|
{% set id = 'sf-call-stack-' ~ log_index %}
|
||||||
|
<a href="#" onclick="Sfjs.toggle('{{ id }}', document.getElementById('{{ id }}-on'), document.getElementById('{{ id }}-off')); return false;">
|
||||||
|
<img class="toggle" id="{{ id }}-off" alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display:none" />
|
||||||
|
<img class="toggle" id="{{ id }}-on" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display:inline" />
|
||||||
|
</a>
|
||||||
|
{% for index, call in log.context.stack if index > 0 %}
|
||||||
|
{% if index == 1 %}
|
||||||
|
<ul class="sf-call-stack" id="{{ id }}" style="display: none">
|
||||||
|
{% endif %}
|
||||||
|
{% if call.class is defined %}
|
||||||
|
{% set from = call.class|abbr_class ~ '::' ~ call.function|abbr_method() %}
|
||||||
|
{% elseif call.function is defined %}
|
||||||
|
{% set from = call.function|abbr_method %}
|
||||||
|
{% elseif call.file is defined %}
|
||||||
|
{% set from = call.file %}
|
||||||
|
{% else %}
|
||||||
|
{% set from = '-' %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<li>Called from {{ call.file is defined and call.line is defined ? call.file|format_file(call.line, from) : from|raw }}</li>
|
||||||
|
|
||||||
|
{{ index == log.context.stack|length - 1 ? '</ul>' : '' }}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{ log.priorityName }} - {{ log.message }}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<div id="collector-wrapper">
|
<div id="collector-wrapper">
|
||||||
{% if profile %}
|
{% if profile %}
|
||||||
<div id="resume">
|
<div id="resume">
|
||||||
<a id="resume-view-all" href="{{ path('_profiler_search', {limit: 10}) }}">View all</a>
|
<a id="resume-view-all" href="{{ path('_profiler_search', {limit: 10}) }}">View last 10</a>
|
||||||
<strong>Profile for:</strong>
|
<strong>Profile for:</strong>
|
||||||
{{ profile.method|upper }}
|
{{ profile.method|upper }}
|
||||||
{% if profile.method|upper in ['GET', 'HEAD'] %}
|
{% if profile.method|upper in ['GET', 'HEAD'] %}
|
||||||
|
|
|
@ -273,6 +273,10 @@ ul.alt li.warning {
|
||||||
background-color: #ffcc00;
|
background-color: #ffcc00;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
}
|
}
|
||||||
|
ul.sf-call-stack li {
|
||||||
|
text-size: small;
|
||||||
|
padding: 0 0 0 20px;
|
||||||
|
}
|
||||||
td.main, td.menu {
|
td.main, td.menu {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of the Symfony package.
|
|
||||||
*
|
|
||||||
* (c) Fabien Potencier <fabien@symfony.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Symfony\Bundle\WebProfilerBundle\Tests\Controller;
|
|
||||||
|
|
||||||
use Symfony\Bundle\WebProfilerBundle\Tests\TestCase;
|
|
||||||
|
|
||||||
use Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController;
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|
||||||
use Symfony\Component\DependencyInjection\Scope;
|
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
|
||||||
|
|
||||||
class ExceptionControllerTest extends TestCase
|
|
||||||
{
|
|
||||||
protected $controller;
|
|
||||||
protected $container;
|
|
||||||
protected $flatten;
|
|
||||||
protected $kernel;
|
|
||||||
|
|
||||||
protected function setUp()
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$this->flatten = $this->getMock('Symfony\Component\HttpKernel\Exception\FlattenException');
|
|
||||||
$this->flatten->expects($this->once())->method('getStatusCode')->will($this->returnValue(404));
|
|
||||||
$this->controller = new ExceptionController();
|
|
||||||
$this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface');
|
|
||||||
$this->container = $this->getContainer();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function tearDown()
|
|
||||||
{
|
|
||||||
parent::tearDown();
|
|
||||||
|
|
||||||
$this->controller = null;
|
|
||||||
$this->container = null;
|
|
||||||
$this->flatten = null;
|
|
||||||
$this->kernel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getDebugModes
|
|
||||||
*/
|
|
||||||
public function testShowActionDependingOnDebug($debug)
|
|
||||||
{
|
|
||||||
$this->container->setParameter('kernel.debug', $debug);
|
|
||||||
$this->controller->setContainer($this->container);
|
|
||||||
$this->controller->showAction($this->flatten);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDebugModes()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
array(true),
|
|
||||||
array(false),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getContainer()
|
|
||||||
{
|
|
||||||
$container = new ContainerBuilder();
|
|
||||||
$container->addScope(new Scope('request'));
|
|
||||||
$container->register('request', 'Symfony\\Component\\HttpFoundation\\Request')->setScope('request');
|
|
||||||
$container->register('templating.helper.assets', $this->getMockClass('Symfony\\Component\\Templating\\Helper\\AssetsHelper'));
|
|
||||||
$container->register('templating.helper.router', $this->getMockClass('Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper'))
|
|
||||||
->addArgument(new Definition($this->getMockClass('Symfony\\Component\\Routing\\RouterInterface')));
|
|
||||||
$container->register('twig', 'Twig_Environment');
|
|
||||||
$container->register('templating.engine.twig', $this->getMockClass('Symfony\\Bundle\\TwigBundle\\TwigEngine'))
|
|
||||||
->addArgument($this->getMock('Twig_Environment'))
|
|
||||||
->addArgument($this->getMock('Symfony\\Component\\Templating\\TemplateNameParserInterface'))
|
|
||||||
->addArgument(new Definition($this->getMockClass('Symfony\Component\Config\FileLocatorInterface')))
|
|
||||||
->addArgument($this->getMock('Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', array(), array($this->getMock('Symfony\\Component\\DependencyInjection\\Container'))));
|
|
||||||
$container->setAlias('templating', 'templating.engine.twig');
|
|
||||||
$container->setParameter('kernel.bundles', array());
|
|
||||||
$container->setParameter('kernel.cache_dir', __DIR__);
|
|
||||||
$container->setParameter('kernel.root_dir', __DIR__);
|
|
||||||
$container->set('kernel', $this->kernel);
|
|
||||||
|
|
||||||
return $container;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -89,7 +89,6 @@ class TemplateManagerTest extends TestCase
|
||||||
*/
|
*/
|
||||||
public function testGetTemplates()
|
public function testGetTemplates()
|
||||||
{
|
{
|
||||||
|
|
||||||
$profile = $this->mockProfile();
|
$profile = $this->mockProfile();
|
||||||
$profile->expects($this->any())
|
$profile->expects($this->any())
|
||||||
->method('hasCollector')
|
->method('hasCollector')
|
||||||
|
@ -145,6 +144,10 @@ class TemplateManagerTest extends TestCase
|
||||||
->method('loadTemplate')
|
->method('loadTemplate')
|
||||||
->will($this->returnValue('loadedTemplate'));
|
->will($this->returnValue('loadedTemplate'));
|
||||||
|
|
||||||
|
$this->twigEnvironment->expects($this->any())
|
||||||
|
->method('getLoader')
|
||||||
|
->will($this->returnValue($this->getMock('\Twig_LoaderInterface')));
|
||||||
|
|
||||||
return $this->twigEnvironment;
|
return $this->twigEnvironment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,14 @@
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.3",
|
"php": ">=5.3.3",
|
||||||
"symfony/twig-bundle": "2.2.*"
|
"symfony/http-kernel": "2.2.*",
|
||||||
|
"symfony/routing": "2.2.*",
|
||||||
|
"symfony/twig-bridge": "2.2.*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/config": "2.2.*",
|
||||||
|
"symfony/dependency-injection": "2.2.*",
|
||||||
|
"symfony/stopwatch": "2.2.*"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": { "Symfony\\Bundle\\WebProfilerBundle\\": "" }
|
"psr-0": { "Symfony\\Bundle\\WebProfilerBundle\\": "" }
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
syntaxCheck="false"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Symfony WebProfilerBundle Test Suite">
|
||||||
|
<directory>./Tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./</directory>
|
||||||
|
<exclude>
|
||||||
|
<directory>./Tests</directory>
|
||||||
|
<directory>./Resources</directory>
|
||||||
|
<directory>./vendor</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Tests\BrowserKit;
|
namespace Symfony\Component\BrowserKit\Tests;
|
||||||
|
|
||||||
use Symfony\Component\BrowserKit\Client;
|
use Symfony\Component\BrowserKit\Client;
|
||||||
use Symfony\Component\BrowserKit\History;
|
use Symfony\Component\BrowserKit\History;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Tests\BrowserKit;
|
namespace Symfony\Component\BrowserKit\Tests;
|
||||||
|
|
||||||
use Symfony\Component\BrowserKit\CookieJar;
|
use Symfony\Component\BrowserKit\CookieJar;
|
||||||
use Symfony\Component\BrowserKit\Cookie;
|
use Symfony\Component\BrowserKit\Cookie;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Tests\BrowserKit;
|
namespace Symfony\Component\BrowserKit\Tests;
|
||||||
|
|
||||||
use Symfony\Component\BrowserKit\Cookie;
|
use Symfony\Component\BrowserKit\Cookie;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Tests\BrowserKit;
|
namespace Symfony\Component\BrowserKit\Tests;
|
||||||
|
|
||||||
use Symfony\Component\BrowserKit\History;
|
use Symfony\Component\BrowserKit\History;
|
||||||
use Symfony\Component\BrowserKit\Request;
|
use Symfony\Component\BrowserKit\Request;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Tests\BrowserKit;
|
namespace Symfony\Component\BrowserKit\Tests;
|
||||||
|
|
||||||
use Symfony\Component\BrowserKit\Request;
|
use Symfony\Component\BrowserKit\Request;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Tests\BrowserKit;
|
namespace Symfony\Component\BrowserKit\Tests;
|
||||||
|
|
||||||
use Symfony\Component\BrowserKit\Response;
|
use Symfony\Component\BrowserKit\Response;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ CHANGELOG
|
||||||
|
|
||||||
* added numerical type handling for config definitions
|
* added numerical type handling for config definitions
|
||||||
* added convenience methods for optional configuration sections to ArrayNodeDefinition
|
* added convenience methods for optional configuration sections to ArrayNodeDefinition
|
||||||
|
* added a utils class for XML manipulations
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -23,12 +23,15 @@ class Processor
|
||||||
*
|
*
|
||||||
* @param NodeInterface $configTree The node tree describing the configuration
|
* @param NodeInterface $configTree The node tree describing the configuration
|
||||||
* @param array $configs An array of configuration items to process
|
* @param array $configs An array of configuration items to process
|
||||||
|
* @param bool $normalizeKeys Flag indicating if config key normalization is needed. True by default.
|
||||||
*
|
*
|
||||||
* @return array The processed configuration
|
* @return array The processed configuration
|
||||||
*/
|
*/
|
||||||
public function process(NodeInterface $configTree, array $configs)
|
public function process(NodeInterface $configTree, array $configs, $normalizeKeys = true)
|
||||||
{
|
{
|
||||||
|
if ($normalizeKeys) {
|
||||||
$configs = self::normalizeKeys($configs);
|
$configs = self::normalizeKeys($configs);
|
||||||
|
}
|
||||||
|
|
||||||
$currentConfig = array();
|
$currentConfig = array();
|
||||||
foreach ($configs as $config) {
|
foreach ($configs as $config) {
|
||||||
|
@ -44,12 +47,13 @@ class Processor
|
||||||
*
|
*
|
||||||
* @param ConfigurationInterface $configuration The configuration class
|
* @param ConfigurationInterface $configuration The configuration class
|
||||||
* @param array $configs An array of configuration items to process
|
* @param array $configs An array of configuration items to process
|
||||||
|
* @param bool $normalizeKeys Flag indicating if config key normalization is needed. True by default.
|
||||||
*
|
*
|
||||||
* @return array The processed configuration
|
* @return array The processed configuration
|
||||||
*/
|
*/
|
||||||
public function processConfiguration(ConfigurationInterface $configuration, array $configs)
|
public function processConfiguration(ConfigurationInterface $configuration, array $configs, $normalizeKeys = true)
|
||||||
{
|
{
|
||||||
return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs);
|
return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs, $normalizeKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,10 +47,12 @@ abstract class Loader implements LoaderInterface
|
||||||
*
|
*
|
||||||
* @param mixed $resource A Resource
|
* @param mixed $resource A Resource
|
||||||
* @param string $type The resource type
|
* @param string $type The resource type
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function import($resource, $type = null)
|
public function import($resource, $type = null)
|
||||||
{
|
{
|
||||||
$this->resolve($resource)->load($resource, $type);
|
return $this->resolve($resource)->load($resource, $type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE scan [<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource={{ resource }}">]>
|
||||||
|
<scan></scan>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root2 xmlns="http://example.com/schema" />
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<xsd:schema xmlns="http://example.com/schema"
|
||||||
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://example.com/schema"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
|
||||||
|
<xsd:element name="root" />
|
||||||
|
</xsd:schema>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root xmlns="http://example.com/schema">
|
||||||
|
</root>
|
|
@ -55,6 +55,15 @@ class LoaderTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderLoadException', $e, '->resolve() throws a FileLoaderLoadException if the resource cannot be loaded');
|
$this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderLoadException', $e, '->resolve() throws a FileLoaderLoadException if the resource cannot be loaded');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testImport()
|
||||||
|
{
|
||||||
|
$loader = $this->getMock('Symfony\Component\Config\Loader\Loader', array('supports', 'load'));
|
||||||
|
$loader->expects($this->once())->method('supports')->will($this->returnValue(true));
|
||||||
|
$loader->expects($this->once())->method('load')->will($this->returnValue('yes'));
|
||||||
|
|
||||||
|
$this->assertEquals('yes', $loader->import('foo'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProjectLoader1 extends Loader
|
class ProjectLoader1 extends Loader
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Tests\Loader;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
|
|
||||||
|
class XmlUtilsTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testLoadFile()
|
||||||
|
{
|
||||||
|
$fixtures = __DIR__.'/../Fixtures/Util/';
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'invalid.xml');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('ERROR 77', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'document_type.xml');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('Document types are not allowed', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('ERROR 1845', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('XSD file or callable', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$mock = $this->getMock(__NAMESPACE__.'\Validator');
|
||||||
|
$mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true));
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'));
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('is not valid', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getDataForConvertDomToArray
|
||||||
|
*/
|
||||||
|
public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true)
|
||||||
|
{
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadXML($root ? $xml : '<root>'.$xml.'</root>');
|
||||||
|
|
||||||
|
$this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataForConvertDomToArray()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(null, ''),
|
||||||
|
array('bar', 'bar'),
|
||||||
|
array(array('bar' => 'foobar'), '<foo bar="foobar" />', true),
|
||||||
|
array(array('foo' => null), '<foo />'),
|
||||||
|
array(array('foo' => 'bar'), '<foo>bar</foo>'),
|
||||||
|
array(array('foo' => array('foo' => 'bar')), '<foo foo="bar"/>'),
|
||||||
|
array(array('foo' => array('foo' => 'bar')), '<foo><foo>bar</foo></foo>'),
|
||||||
|
array(array('foo' => array('foo' => 'bar', 'value' => 'text')), '<foo foo="bar">text</foo>'),
|
||||||
|
array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), '<foo attr="bar"><foo>text</foo></foo>'),
|
||||||
|
array(array('foo' => array('bar', 'text')), '<foo>bar</foo><foo>text</foo>'),
|
||||||
|
array(array('foo' => array(array('foo' => 'bar'), array('foo' => 'text'))), '<foo foo="bar"/><foo foo="text" />'),
|
||||||
|
array(array('foo' => array('foo' => array('bar', 'text'))), '<foo foo="bar"><foo>text</foo></foo>'),
|
||||||
|
array(array('foo' => 'bar'), '<foo><!-- Comment -->bar</foo>'),
|
||||||
|
array(array('foo' => 'text'), '<foo xmlns:h="http://www.example.org/bar" h:bar="bar">text</foo>'),
|
||||||
|
array(array('foo' => array('bar' => 'bar', 'value' => 'text')), '<foo xmlns:h="http://www.example.org/bar" h:bar="bar">text</foo>', false, false),
|
||||||
|
array(array('attr' => 1, 'b' => 'hello'), '<foo:a xmlns:foo="http://www.example.org/foo" xmlns:h="http://www.example.org/bar" attr="1" h:bar="bar"><foo:b>hello</foo:b><h:c>2</h:c></foo:a>', true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getDataForPhpize
|
||||||
|
*/
|
||||||
|
public function testPhpize($expected, $value)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, XmlUtils::phpize($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataForPhpize()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(null, 'null'),
|
||||||
|
array(true, 'true'),
|
||||||
|
array(false, 'false'),
|
||||||
|
array(null, 'Null'),
|
||||||
|
array(true, 'True'),
|
||||||
|
array(false, 'False'),
|
||||||
|
array(0, '0'),
|
||||||
|
array(1, '1'),
|
||||||
|
array(0777, '0777'),
|
||||||
|
array(255, '0xFF'),
|
||||||
|
array(100.0, '1e2'),
|
||||||
|
array(-120.0, '-1.2E2'),
|
||||||
|
array(-10100.1, '-10100.1'),
|
||||||
|
array(-10100.1, '-10,100.1'),
|
||||||
|
array('foo', 'foo'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Validator
|
||||||
|
{
|
||||||
|
public function validate();
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XMLUtils is a bunch of utility methods to XML operations.
|
||||||
|
*
|
||||||
|
* This class contains static methods only and is not meant to be instantiated.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||||
|
*/
|
||||||
|
class XmlUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class should not be instantiated
|
||||||
|
*/
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an XML file.
|
||||||
|
*
|
||||||
|
* @param string $file An XML file path
|
||||||
|
* @param string|callable $schemaOrCallable An XSD schema file path or callable
|
||||||
|
*
|
||||||
|
* @return \DOMDocument
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When loading of XML file returns error
|
||||||
|
*/
|
||||||
|
public static function loadFile($file, $schemaOrCallable = null)
|
||||||
|
{
|
||||||
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
|
$disableEntities = libxml_disable_entity_loader(true);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->validateOnParse = true;
|
||||||
|
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$dom->normalizeDocument();
|
||||||
|
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
|
foreach ($dom->childNodes as $child) {
|
||||||
|
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
||||||
|
throw new \InvalidArgumentException('Document types are not allowed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $schemaOrCallable) {
|
||||||
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
$e = null;
|
||||||
|
if (is_callable($schemaOrCallable)) {
|
||||||
|
try {
|
||||||
|
$valid = call_user_func($schemaOrCallable, $dom, $internalErrors);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
} elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) {
|
||||||
|
$valid = @$dom->schemaValidate($schemaOrCallable);
|
||||||
|
} else {
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$valid) {
|
||||||
|
$messages = static::getXmlErrors($internalErrors);
|
||||||
|
if (empty($messages)) {
|
||||||
|
$messages = array(sprintf('The XML file "%s" is not valid.', $file));
|
||||||
|
}
|
||||||
|
throw new \InvalidArgumentException(implode("\n", $messages), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a \DomElement object to a PHP array.
|
||||||
|
*
|
||||||
|
* The following rules applies during the conversion:
|
||||||
|
*
|
||||||
|
* * Each tag is converted to a key value or an array
|
||||||
|
* if there is more than one "value"
|
||||||
|
*
|
||||||
|
* * The content of a tag is set under a "value" key (<foo>bar</foo>)
|
||||||
|
* if the tag also has some nested tags
|
||||||
|
*
|
||||||
|
* * The attributes are converted to keys (<foo foo="bar"/>)
|
||||||
|
*
|
||||||
|
* * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
|
||||||
|
*
|
||||||
|
* @param \DomElement $element A \DomElement instance
|
||||||
|
* @param Boolean $checkPrefix Check prefix in an element or an attribute name
|
||||||
|
*
|
||||||
|
* @return array A PHP array
|
||||||
|
*/
|
||||||
|
public static function convertDomElementToArray(\DomElement $element, $checkPrefix = true)
|
||||||
|
{
|
||||||
|
$prefix = (string) $element->prefix;
|
||||||
|
$empty = true;
|
||||||
|
$config = array();
|
||||||
|
foreach ($element->attributes as $name => $node) {
|
||||||
|
if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$config[$name] = static::phpize($node->value);
|
||||||
|
$empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nodeValue = false;
|
||||||
|
foreach ($element->childNodes as $node) {
|
||||||
|
if ($node instanceof \DOMText) {
|
||||||
|
if (trim($node->nodeValue)) {
|
||||||
|
$nodeValue = trim($node->nodeValue);
|
||||||
|
$empty = false;
|
||||||
|
}
|
||||||
|
} elseif ($checkPrefix && $prefix != (string) $node->prefix) {
|
||||||
|
continue;
|
||||||
|
} elseif (!$node instanceof \DOMComment) {
|
||||||
|
$value = static::convertDomElementToArray($node, $checkPrefix);
|
||||||
|
|
||||||
|
$key = $node->localName;
|
||||||
|
if (isset($config[$key])) {
|
||||||
|
if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
|
||||||
|
$config[$key] = array($config[$key]);
|
||||||
|
}
|
||||||
|
$config[$key][] = $value;
|
||||||
|
} else {
|
||||||
|
$config[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$empty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $nodeValue) {
|
||||||
|
$value = static::phpize($nodeValue);
|
||||||
|
if (count($config)) {
|
||||||
|
$config['value'] = $value;
|
||||||
|
} else {
|
||||||
|
$config = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$empty ? $config : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an xml value to a php type.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function phpize($value)
|
||||||
|
{
|
||||||
|
$value = (string) $value;
|
||||||
|
$lowercaseValue = strtolower($value);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case 'null' === $lowercaseValue:
|
||||||
|
return null;
|
||||||
|
case ctype_digit($value):
|
||||||
|
$raw = $value;
|
||||||
|
$cast = intval($value);
|
||||||
|
|
||||||
|
return '0' == $value[0] ? octdec($value) : (((string) $raw == (string) $cast) ? $cast : $raw);
|
||||||
|
case 'true' === $lowercaseValue:
|
||||||
|
return true;
|
||||||
|
case 'false' === $lowercaseValue:
|
||||||
|
return false;
|
||||||
|
case is_numeric($value):
|
||||||
|
return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value);
|
||||||
|
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $value):
|
||||||
|
return floatval(str_replace(',', '', $value));
|
||||||
|
default:
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getXmlErrors($internalErrors)
|
||||||
|
{
|
||||||
|
$errors = array();
|
||||||
|
foreach (libxml_get_errors() as $error) {
|
||||||
|
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
||||||
|
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
||||||
|
$error->code,
|
||||||
|
trim($error->message),
|
||||||
|
$error->file ? $error->file : 'n/a',
|
||||||
|
$error->line,
|
||||||
|
$error->column
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
libxml_clear_errors();
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -866,7 +866,7 @@ class Application
|
||||||
return preg_replace('{^(\d+)x.*$}', '$1', $ansicon);
|
return preg_replace('{^(\d+)x.*$}', '$1', $ansicon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('{columns:\s*(\d+)}i', $this->getConsoleMode(), $matches)) {
|
if (preg_match('{^(\d+)x\d+$}i', $this->getConsoleMode(), $matches)) {
|
||||||
return $matches[1];
|
return $matches[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -888,7 +888,7 @@ class Application
|
||||||
return preg_replace('{^\d+x\d+ \(\d+x(\d+)\)$}', '$1', trim($ansicon));
|
return preg_replace('{^\d+x\d+ \(\d+x(\d+)\)$}', '$1', trim($ansicon));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('{lines:\s*(\d+)}i', $this->getConsoleMode(), $matches)) {
|
if (preg_match('{^\d+x(\d+)$}i', $this->getConsoleMode(), $matches)) {
|
||||||
return $matches[1];
|
return $matches[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -980,7 +980,7 @@ class Application
|
||||||
/**
|
/**
|
||||||
* Runs and parses mode CON if it's available, suppressing any error output
|
* Runs and parses mode CON if it's available, suppressing any error output
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string <width>x<height> or null if it could not be parsed
|
||||||
*/
|
*/
|
||||||
private function getConsoleMode()
|
private function getConsoleMode()
|
||||||
{
|
{
|
||||||
|
@ -996,7 +996,9 @@ class Application
|
||||||
fclose($pipes[2]);
|
fclose($pipes[2]);
|
||||||
proc_close($process);
|
proc_close($process);
|
||||||
|
|
||||||
return $info;
|
if (preg_match('{--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n}', $info, $matches)) {
|
||||||
|
return $matches[2].'x'.$matches[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ CHANGELOG
|
||||||
|
|
||||||
* added support for colorization on Windows via ConEmu
|
* added support for colorization on Windows via ConEmu
|
||||||
* add a method to Dialog Helper to ask for a question and hide the response
|
* add a method to Dialog Helper to ask for a question and hide the response
|
||||||
|
* added support for interactive selections in console (DialogHelper::select())
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -247,7 +247,7 @@ class Command
|
||||||
* If this method is used, it overrides the code defined
|
* If this method is used, it overrides the code defined
|
||||||
* in the execute() method.
|
* in the execute() method.
|
||||||
*
|
*
|
||||||
* @param \Closure $code A \Closure
|
* @param callable $code A callable(InputInterface $input, OutputInterface $output)
|
||||||
*
|
*
|
||||||
* @return Command The current instance
|
* @return Command The current instance
|
||||||
*
|
*
|
||||||
|
@ -255,8 +255,12 @@ class Command
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function setCode(\Closure $code)
|
public function setCode($code)
|
||||||
{
|
{
|
||||||
|
if (!is_callable($code)) {
|
||||||
|
throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
|
||||||
|
}
|
||||||
|
|
||||||
$this->code = $code;
|
$this->code = $code;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
@ -24,6 +24,40 @@ class DialogHelper extends Helper
|
||||||
private static $shell;
|
private static $shell;
|
||||||
private static $stty;
|
private static $stty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the user to select a value.
|
||||||
|
*
|
||||||
|
* @param OutputInterface $output An Output instance
|
||||||
|
* @param string|array $question The question to ask
|
||||||
|
* @param array $choices List of choices to pick from
|
||||||
|
* @param Boolean $default The default answer if the user enters nothing
|
||||||
|
* @param integer|false $attempts Max number of times to ask before giving up (false by default, which means infinite)
|
||||||
|
* @param string $errorMessage Message which will be shown if invalid value from choice list would be picked
|
||||||
|
*
|
||||||
|
* @return integer|string The selected value (the key of the choices array)
|
||||||
|
*/
|
||||||
|
public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid')
|
||||||
|
{
|
||||||
|
$width = max(array_map('strlen', array_keys($choices)));
|
||||||
|
|
||||||
|
$messages = (array) $question;
|
||||||
|
foreach ($choices as $key => $value) {
|
||||||
|
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output->writeln($messages);
|
||||||
|
|
||||||
|
$result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage) {
|
||||||
|
if (empty($choices[$picked])) {
|
||||||
|
throw new \InvalidArgumentException(sprintf($errorMessage, $picked));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $picked;
|
||||||
|
}, $attempts, $default);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks a question to the user.
|
* Asks a question to the user.
|
||||||
*
|
*
|
||||||
|
|
|
@ -36,6 +36,7 @@ class ProgressHelper extends Helper
|
||||||
private $format = null;
|
private $format = null;
|
||||||
private $redrawFreq = 1;
|
private $redrawFreq = 1;
|
||||||
|
|
||||||
|
private $lastMessagesLength;
|
||||||
private $barCharOriginal;
|
private $barCharOriginal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -380,21 +381,20 @@ class ProgressHelper extends Helper
|
||||||
*
|
*
|
||||||
* @param OutputInterface $output An Output instance
|
* @param OutputInterface $output An Output instance
|
||||||
* @param string|array $messages The message as an array of lines or a single string
|
* @param string|array $messages The message as an array of lines or a single string
|
||||||
* @param Boolean $newline Whether to add a newline or not
|
|
||||||
* @param integer $size The size of line
|
|
||||||
*/
|
*/
|
||||||
private function overwrite(OutputInterface $output, $messages, $newline = false, $size = 80)
|
private function overwrite(OutputInterface $output, $messages)
|
||||||
{
|
{
|
||||||
$output->write(str_repeat("\x08", $size));
|
// carriage return
|
||||||
$output->write($messages);
|
$output->write("\x0D");
|
||||||
$output->write(str_repeat(' ', $size - strlen($messages)));
|
if ($this->lastMessagesLength!==null) {
|
||||||
|
// clear the line with the length of the last message
|
||||||
// clean up the end line
|
$output->write(str_repeat("\x20", $this->lastMessagesLength));
|
||||||
$output->write(str_repeat("\x08", $size - strlen($messages)));
|
// carriage return
|
||||||
|
$output->write("\x0D");
|
||||||
if ($newline) {
|
|
||||||
$output->writeln('');
|
|
||||||
}
|
}
|
||||||
|
$output->write($messages);
|
||||||
|
|
||||||
|
$this->lastMessagesLength=strlen($messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -242,7 +242,6 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertRegExp('/Did you mean this/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with one alternative');
|
$this->assertRegExp('/Did you mean this/', $e->getMessage(), '->find() throws an \InvalidArgumentException if command does not exist, with one alternative');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$application->add(new \Foo1Command());
|
$application->add(new \Foo1Command());
|
||||||
$application->add(new \Foo2Command());
|
$application->add(new \Foo2Command());
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,31 @@ class CommandTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
|
$this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetCodeWithNonClosureCallable()
|
||||||
|
{
|
||||||
|
$command = new \TestCommand();
|
||||||
|
$ret = $command->setCode(array($this, 'callableMethodCommand'));
|
||||||
|
$this->assertEquals($command, $ret, '->setCode() implements a fluent interface');
|
||||||
|
$tester = new CommandTester($command);
|
||||||
|
$tester->execute(array());
|
||||||
|
$this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException InvalidArgumentException
|
||||||
|
* @expectedExceptionMessage Invalid callable provided to Command::setCode.
|
||||||
|
*/
|
||||||
|
public function testSetCodeWithNonCallable()
|
||||||
|
{
|
||||||
|
$command = new \TestCommand();
|
||||||
|
$command->setCode(array($this, 'nonExistentMethod'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callableMethodCommand(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$output->writeln('from the code...');
|
||||||
|
}
|
||||||
|
|
||||||
public function testAsText()
|
public function testAsText()
|
||||||
{
|
{
|
||||||
$command = new \TestCommand();
|
$command = new \TestCommand();
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace Symfony\Component\Console\Tests\Formatter;
|
namespace Symfony\Component\Console\Tests\Formatter;
|
||||||
|
|
||||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||||
|
|
|
@ -18,6 +18,31 @@ use Symfony\Component\Console\Output\StreamOutput;
|
||||||
|
|
||||||
class DialogHelperTest extends \PHPUnit_Framework_TestCase
|
class DialogHelperTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
public function testSelect()
|
||||||
|
{
|
||||||
|
$dialog = new DialogHelper();
|
||||||
|
|
||||||
|
$helperSet = new HelperSet(array(new FormatterHelper()));
|
||||||
|
$dialog->setHelperSet($helperSet);
|
||||||
|
|
||||||
|
$heroes = array('Superman', 'Batman', 'Spiderman');
|
||||||
|
|
||||||
|
$dialog->setInputStream($this->getInputStream("\n1\nFabien\n1\nFabien\nFabien\n"));
|
||||||
|
$this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2'));
|
||||||
|
$this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes));
|
||||||
|
$this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!'));
|
||||||
|
|
||||||
|
rewind($output->getStream());
|
||||||
|
$this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1));
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function testAsk()
|
public function testAsk()
|
||||||
{
|
{
|
||||||
$dialog = new DialogHelper();
|
$dialog = new DialogHelper();
|
||||||
|
|
|
@ -79,8 +79,18 @@ class ProgressHelperTest extends \PHPUnit_Framework_TestCase
|
||||||
return new StreamOutput(fopen('php://memory', 'r+', false));
|
return new StreamOutput(fopen('php://memory', 'r+', false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected $lastMessagesLength;
|
||||||
|
|
||||||
protected function generateOutput($expected)
|
protected function generateOutput($expected)
|
||||||
{
|
{
|
||||||
return str_repeat("\x08", 80).$expected.str_repeat(' ', 80 - strlen($expected)).str_repeat("\x08", 80 - strlen($expected));
|
$expectedout = $expected;
|
||||||
|
|
||||||
|
if ($this->lastMessagesLength !== null) {
|
||||||
|
$expectedout = str_repeat("\x20", $this->lastMessagesLength)."\x0D".$expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->lastMessagesLength = strlen($expected);
|
||||||
|
|
||||||
|
return "\x0D".$expectedout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,6 @@ class FunctionNode implements NodeInterface
|
||||||
$xpath->addCondition(sprintf('contains(string(.), %s)', XPathExpr::xpathLiteral($expr)));
|
$xpath->addCondition(sprintf('contains(string(.), %s)', XPathExpr::xpathLiteral($expr)));
|
||||||
|
|
||||||
// FIXME: Currently case insensitive matching doesn't seem to be happening
|
// FIXME: Currently case insensitive matching doesn't seem to be happening
|
||||||
|
|
||||||
return $xpath;
|
return $xpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +263,6 @@ class FunctionNode implements NodeInterface
|
||||||
|
|
||||||
if (false === strpos($s, 'n')) {
|
if (false === strpos($s, 'n')) {
|
||||||
// Just a b
|
// Just a b
|
||||||
|
|
||||||
return array(0, intval((string) $s));
|
return array(0, intval((string) $s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
2.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added an Extension base class with sensible defaults to be used in conjunction
|
||||||
|
with the Config component.
|
||||||
|
* added PrependExtensionInterface (to be able to allow extensions to prepend
|
||||||
|
application configuration settings for any Bundle)
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -12,4 +20,3 @@ CHANGELOG
|
||||||
(this includes dumped containers)
|
(this includes dumped containers)
|
||||||
* [BC BREAK] fixed unescaping of class arguments, method
|
* [BC BREAK] fixed unescaping of class arguments, method
|
||||||
ParameterBag::unescapeValue() was made public
|
ParameterBag::unescapeValue() was made public
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,12 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
|
||||||
$definitions = $container->getDefinitions();
|
$definitions = $container->getDefinitions();
|
||||||
$aliases = $container->getAliases();
|
$aliases = $container->getAliases();
|
||||||
|
|
||||||
|
foreach ($container->getExtensions() as $extension) {
|
||||||
|
if ($extension instanceof PrependExtensionInterface) {
|
||||||
|
$extension->prepend($container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($container->getExtensions() as $name => $extension) {
|
foreach ($container->getExtensions() as $name => $extension) {
|
||||||
if (!$config = $container->getExtensionConfig($name)) {
|
if (!$config = $container->getExtensionConfig($name)) {
|
||||||
// this extension was not called
|
// this extension was not called
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
|
interface PrependExtensionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Allow an extension to prepend the extension configurations.
|
||||||
|
*
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*/
|
||||||
|
public function prepend(ContainerBuilder $container);
|
||||||
|
}
|
|
@ -465,6 +465,21 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||||
return $this->extensionConfigs[$name];
|
return $this->extensionConfigs[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends a config array to the configs of the given extension.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the extension
|
||||||
|
* @param array $config The config to set
|
||||||
|
*/
|
||||||
|
public function prependExtensionConfig($name, array $config)
|
||||||
|
{
|
||||||
|
if (!isset($this->extensionConfigs[$name])) {
|
||||||
|
$this->extensionConfigs[$name] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
array_unshift($this->extensionConfigs[$name], $config);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles the container.
|
* Compiles the container.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\DependencyInjection\Extension;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Container;
|
||||||
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\Config\Definition\Processor;
|
||||||
|
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides useful features shared by many extensions.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the base path for the XSD files.
|
||||||
|
*
|
||||||
|
* @return string The XSD base path
|
||||||
|
*/
|
||||||
|
public function getXsdValidationBasePath()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the namespace to be used for this extension (XML namespace).
|
||||||
|
*
|
||||||
|
* @return string The XML namespace
|
||||||
|
*/
|
||||||
|
public function getNamespace()
|
||||||
|
{
|
||||||
|
return 'http://example.org/schema/dic/'.$this->getAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the recommended alias to use in XML.
|
||||||
|
*
|
||||||
|
* This alias is also the mandatory prefix to use when using YAML.
|
||||||
|
*
|
||||||
|
* This convention is to remove the "Extension" postfix from the class
|
||||||
|
* name and then lowercase and underscore the result. So:
|
||||||
|
*
|
||||||
|
* AcmeHelloExtension
|
||||||
|
*
|
||||||
|
* becomes
|
||||||
|
*
|
||||||
|
* acme_hello
|
||||||
|
*
|
||||||
|
* This can be overridden in a sub-class to specify the alias manually.
|
||||||
|
*
|
||||||
|
* @return string The alias
|
||||||
|
*
|
||||||
|
* @throws \BadMethodCallException When the extension name does not follow conventions
|
||||||
|
*/
|
||||||
|
public function getAlias()
|
||||||
|
{
|
||||||
|
$className = get_class($this);
|
||||||
|
if (substr($className, -9) != 'Extension') {
|
||||||
|
throw new \BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.');
|
||||||
|
}
|
||||||
|
$classBaseName = substr(strrchr($className, '\\'), 1, -9);
|
||||||
|
|
||||||
|
return Container::underscore($classBaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function processConfiguration(ConfigurationInterface $configuration, array $configs, $normalizeKeys = true)
|
||||||
|
{
|
||||||
|
$processor = new Processor();
|
||||||
|
|
||||||
|
return $processor->processConfiguration($configuration, $configs, $normalizeKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$reflected = new \ReflectionClass($this);
|
||||||
|
$namespace = $reflected->getNamespaceName();
|
||||||
|
|
||||||
|
$class = $namespace . '\\Configuration';
|
||||||
|
if (class_exists($class)) {
|
||||||
|
$r = new \ReflectionClass($class);
|
||||||
|
$container->addResource(new FileResource($r->getFileName()));
|
||||||
|
|
||||||
|
if (!method_exists($class, '__construct')) {
|
||||||
|
$configuration = new $class();
|
||||||
|
|
||||||
|
return $configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Symfony\Component\DependencyInjection\Loader;
|
namespace Symfony\Component\DependencyInjection\Loader;
|
||||||
|
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\DependencyInjection\Alias;
|
use Symfony\Component\DependencyInjection\Alias;
|
||||||
|
@ -202,33 +203,17 @@ class XmlFileLoader extends FileLoader
|
||||||
*
|
*
|
||||||
* @return SimpleXMLElement
|
* @return SimpleXMLElement
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When loading of XML file returns error
|
* @throws InvalidArgumentException When loading of XML file returns error
|
||||||
*/
|
*/
|
||||||
protected function parseFile($file)
|
protected function parseFile($file)
|
||||||
{
|
{
|
||||||
$internalErrors = libxml_use_internal_errors(true);
|
try {
|
||||||
$disableEntities = libxml_disable_entity_loader(true);
|
$dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
|
||||||
libxml_clear_errors();
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||||
$dom = new \DOMDocument();
|
|
||||||
$dom->validateOnParse = true;
|
|
||||||
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
throw new InvalidArgumentException(implode("\n", $this->getXmlErrors($internalErrors)));
|
|
||||||
}
|
|
||||||
$dom->normalizeDocument();
|
|
||||||
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
foreach ($dom->childNodes as $child) {
|
|
||||||
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
|
||||||
throw new InvalidArgumentException('Document types are not allowed.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validate($dom, $file);
|
$this->validateExtensions($dom, $file);
|
||||||
|
|
||||||
return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
|
return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
|
||||||
}
|
}
|
||||||
|
@ -285,28 +270,14 @@ class XmlFileLoader extends FileLoader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates an XML document.
|
|
||||||
*
|
|
||||||
* @param \DOMDocument $dom
|
|
||||||
* @param string $file
|
|
||||||
*/
|
|
||||||
private function validate(\DOMDocument $dom, $file)
|
|
||||||
{
|
|
||||||
$this->validateSchema($dom, $file);
|
|
||||||
$this->validateExtensions($dom, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a documents XML schema.
|
* Validates a documents XML schema.
|
||||||
*
|
*
|
||||||
* @param \DOMDocument $dom
|
* @param \DOMDocument $dom
|
||||||
* @param string $file
|
|
||||||
*
|
*
|
||||||
* @throws RuntimeException When extension references a non-existent XSD file
|
* @throws RuntimeException When extension references a non-existent XSD file
|
||||||
* @throws InvalidArgumentException When xml doesn't validate its xsd schema
|
|
||||||
*/
|
*/
|
||||||
private function validateSchema(\DOMDocument $dom, $file)
|
public function validateSchema(\DOMDocument $dom)
|
||||||
{
|
{
|
||||||
$schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
|
$schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
|
||||||
|
|
||||||
|
@ -360,18 +331,13 @@ $imports
|
||||||
EOF
|
EOF
|
||||||
;
|
;
|
||||||
|
|
||||||
$current = libxml_use_internal_errors(true);
|
|
||||||
libxml_clear_errors();
|
|
||||||
|
|
||||||
$valid = @$dom->schemaValidateSource($source);
|
$valid = @$dom->schemaValidateSource($source);
|
||||||
|
|
||||||
foreach ($tmpfiles as $tmpfile) {
|
foreach ($tmpfiles as $tmpfile) {
|
||||||
@unlink($tmpfile);
|
@unlink($tmpfile);
|
||||||
}
|
}
|
||||||
if (!$valid) {
|
|
||||||
throw new InvalidArgumentException(implode("\n", $this->getXmlErrors($current)));
|
return $valid;
|
||||||
}
|
|
||||||
libxml_use_internal_errors($current);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,33 +369,6 @@ EOF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of XML errors.
|
|
||||||
*
|
|
||||||
* @param Boolean $internalErrors
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXmlErrors($internalErrors)
|
|
||||||
{
|
|
||||||
$errors = array();
|
|
||||||
foreach (libxml_get_errors() as $error) {
|
|
||||||
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
|
||||||
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
|
||||||
$error->code,
|
|
||||||
trim($error->message),
|
|
||||||
$error->file ? $error->file : 'n/a',
|
|
||||||
$error->line,
|
|
||||||
$error->column
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
libxml_clear_errors();
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads from an extension.
|
* Loads from an extension.
|
||||||
*
|
*
|
||||||
|
@ -472,50 +411,6 @@ EOF
|
||||||
*/
|
*/
|
||||||
public static function convertDomElementToArray(\DomElement $element)
|
public static function convertDomElementToArray(\DomElement $element)
|
||||||
{
|
{
|
||||||
$empty = true;
|
return XmlUtils::convertDomElementToArray($element);
|
||||||
$config = array();
|
|
||||||
foreach ($element->attributes as $name => $node) {
|
|
||||||
$config[$name] = SimpleXMLElement::phpize($node->value);
|
|
||||||
$empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nodeValue = false;
|
|
||||||
foreach ($element->childNodes as $node) {
|
|
||||||
if ($node instanceof \DOMText) {
|
|
||||||
if (trim($node->nodeValue)) {
|
|
||||||
$nodeValue = trim($node->nodeValue);
|
|
||||||
$empty = false;
|
|
||||||
}
|
|
||||||
} elseif (!$node instanceof \DOMComment) {
|
|
||||||
if ($node instanceof \DOMElement && '_services' === $node->nodeName) {
|
|
||||||
$value = new Reference($node->getAttribute('id'));
|
|
||||||
} else {
|
|
||||||
$value = static::convertDomElementToArray($node);
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $node->localName;
|
|
||||||
if (isset($config[$key])) {
|
|
||||||
if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
|
|
||||||
$config[$key] = array($config[$key]);
|
|
||||||
}
|
|
||||||
$config[$key][] = $value;
|
|
||||||
} else {
|
|
||||||
$config[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$empty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false !== $nodeValue) {
|
|
||||||
$value = SimpleXMLElement::phpize($nodeValue);
|
|
||||||
if (count($config)) {
|
|
||||||
$config['value'] = $value;
|
|
||||||
} else {
|
|
||||||
$config = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !$empty ? $config : null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace Symfony\Component\DependencyInjection;
|
namespace Symfony\Component\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SimpleXMLElement class.
|
* SimpleXMLElement class.
|
||||||
*
|
*
|
||||||
|
@ -101,27 +103,6 @@ class SimpleXMLElement extends \SimpleXMLElement
|
||||||
*/
|
*/
|
||||||
public static function phpize($value)
|
public static function phpize($value)
|
||||||
{
|
{
|
||||||
$value = (string) $value;
|
return XmlUtils::phpize($value);
|
||||||
$lowercaseValue = strtolower($value);
|
|
||||||
|
|
||||||
switch (true) {
|
|
||||||
case 'null' === $lowercaseValue:
|
|
||||||
return null;
|
|
||||||
case ctype_digit($value):
|
|
||||||
$raw = $value;
|
|
||||||
$cast = intval($value);
|
|
||||||
|
|
||||||
return '0' == $value[0] ? octdec($value) : (((string) $raw == (string) $cast) ? $cast : $raw);
|
|
||||||
case 'true' === $lowercaseValue:
|
|
||||||
return true;
|
|
||||||
case 'false' === $lowercaseValue:
|
|
||||||
return false;
|
|
||||||
case is_numeric($value):
|
|
||||||
return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value);
|
|
||||||
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $value):
|
|
||||||
return floatval(str_replace(',', '', $value));
|
|
||||||
default:
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -548,6 +548,28 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
|
||||||
$container->compile();
|
$container->compile();
|
||||||
$container->setDefinition('a', new Definition());
|
$container->setDefinition('a', new Definition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::getExtensionConfig
|
||||||
|
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::prependExtensionConfig
|
||||||
|
*/
|
||||||
|
public function testExtensionConfig()
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
|
||||||
|
$configs = $container->getExtensionConfig('foo');
|
||||||
|
$this->assertEmpty($configs);
|
||||||
|
|
||||||
|
$first = array('foo' => 'bar');
|
||||||
|
$container->prependExtensionConfig('foo', $first);
|
||||||
|
$configs = $container->getExtensionConfig('foo');
|
||||||
|
$this->assertEquals(array($first), $configs);
|
||||||
|
|
||||||
|
$second = array('ding' => 'dong');
|
||||||
|
$container->prependExtensionConfig('foo', $second);
|
||||||
|
$configs = $container->getExtensionConfig('foo');
|
||||||
|
$this->assertEquals(array($second, $first), $configs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FooClass {}
|
class FooClass {}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
2.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added a delete option for the mirror() method
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -336,11 +336,30 @@ class Filesystem
|
||||||
* Valid options are:
|
* Valid options are:
|
||||||
* - $options['override'] Whether to override an existing file on copy or not (see copy())
|
* - $options['override'] Whether to override an existing file on copy or not (see copy())
|
||||||
* - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
|
* - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
|
||||||
|
* - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
|
||||||
*
|
*
|
||||||
* @throws IOException When file type is unknown
|
* @throws IOException When file type is unknown
|
||||||
*/
|
*/
|
||||||
public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
|
public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
|
||||||
{
|
{
|
||||||
|
$targetDir = rtrim($targetDir, '/\\');
|
||||||
|
$originDir = rtrim($originDir, '/\\');
|
||||||
|
|
||||||
|
// Iterate in destination folder to remove obsolete entries
|
||||||
|
if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
|
||||||
|
$deleteIterator = $iterator;
|
||||||
|
if (null === $deleteIterator) {
|
||||||
|
$flags = \FilesystemIterator::SKIP_DOTS;
|
||||||
|
$deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
|
||||||
|
}
|
||||||
|
foreach ($deleteIterator as $file) {
|
||||||
|
$origin = str_replace($targetDir, $originDir, $file->getPathname());
|
||||||
|
if (!$this->exists($origin)) {
|
||||||
|
$this->remove($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$copyOnWindows = false;
|
$copyOnWindows = false;
|
||||||
if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
|
if (isset($options['copy_on_windows']) && !function_exists('symlink')) {
|
||||||
$copyOnWindows = $options['copy_on_windows'];
|
$copyOnWindows = $options['copy_on_windows'];
|
||||||
|
@ -351,9 +370,6 @@ class Filesystem
|
||||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
|
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
|
||||||
}
|
}
|
||||||
|
|
||||||
$targetDir = rtrim($targetDir, '/\\');
|
|
||||||
$originDir = rtrim($originDir, '/\\');
|
|
||||||
|
|
||||||
foreach ($iterator as $file) {
|
foreach ($iterator as $file) {
|
||||||
$target = str_replace($originDir, $targetDir, $file->getPathname());
|
$target = str_replace($originDir, $targetDir, $file->getPathname());
|
||||||
|
|
||||||
|
|
|
@ -798,6 +798,24 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue(is_dir($targetPath.'directory'));
|
$this->assertTrue(is_dir($targetPath.'directory'));
|
||||||
$this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1');
|
$this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1');
|
||||||
$this->assertFileEquals($file2, $targetPath.'file2');
|
$this->assertFileEquals($file2, $targetPath.'file2');
|
||||||
|
|
||||||
|
$this->filesystem->remove($file1);
|
||||||
|
|
||||||
|
$this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false));
|
||||||
|
$this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
|
||||||
|
|
||||||
|
$this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
|
||||||
|
$this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
|
||||||
|
|
||||||
|
file_put_contents($file1, 'FILE1');
|
||||||
|
|
||||||
|
$this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
|
||||||
|
$this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
|
||||||
|
|
||||||
|
$this->filesystem->remove($directory);
|
||||||
|
$this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
|
||||||
|
$this->assertFalse($this->filesystem->exists($targetPath.'directory'));
|
||||||
|
$this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMirrorCopiesLinks()
|
public function testMirrorCopiesLinks()
|
||||||
|
|
|
@ -34,6 +34,22 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||||
protected $paths = array();
|
protected $paths = array();
|
||||||
protected $notPaths = array();
|
protected $notPaths = array();
|
||||||
|
|
||||||
|
private static $areSupported = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function isSupported()
|
||||||
|
{
|
||||||
|
$name = $this->getName();
|
||||||
|
|
||||||
|
if (!array_key_exists($name, self::$areSupported)) {
|
||||||
|
self::$areSupported[$name] = $this->canBeUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$areSupported[$name];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -193,4 +209,17 @@ abstract class AbstractAdapter implements AdapterInterface
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the adapter is supported in the current environment.
|
||||||
|
*
|
||||||
|
* This method should be implemented in all adapters. Do not implement
|
||||||
|
* isSupported in the adapters as the generic implementation provides a cache
|
||||||
|
* layer.
|
||||||
|
*
|
||||||
|
* @see isSupported
|
||||||
|
*
|
||||||
|
* @return Boolean Whether the adapter is supported
|
||||||
|
*/
|
||||||
|
abstract protected function canBeUsed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,13 +118,14 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function isSupported()
|
protected function canBeUsed()
|
||||||
{
|
{
|
||||||
return $this->shell->testCommand('find');
|
return $this->shell->testCommand('find');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Command $command
|
* @param Command $command
|
||||||
|
* @param string $dir
|
||||||
*
|
*
|
||||||
* @return Command
|
* @return Command
|
||||||
*/
|
*/
|
||||||
|
@ -140,7 +141,7 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
/**
|
/**
|
||||||
* @param Command $command
|
* @param Command $command
|
||||||
* @param string[] $names
|
* @param string[] $names
|
||||||
* @param bool $not
|
* @param Boolean $not
|
||||||
*/
|
*/
|
||||||
private function buildNamesFiltering(Command $command, array $names, $not = false)
|
private function buildNamesFiltering(Command $command, array $names, $not = false)
|
||||||
{
|
{
|
||||||
|
@ -183,7 +184,8 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
* @param Command $command
|
* @param Command $command
|
||||||
* @param string $dir
|
* @param string $dir
|
||||||
* @param string[] $paths
|
* @param string[] $paths
|
||||||
* @param bool $not
|
* @param Boolean $not
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
|
private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
|
||||||
|
@ -226,33 +228,23 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
foreach ($sizes as $i => $size) {
|
foreach ($sizes as $i => $size) {
|
||||||
$command->add($i > 0 ? '-and' : null);
|
$command->add($i > 0 ? '-and' : null);
|
||||||
|
|
||||||
if ('<=' === $size->getOperator()) {
|
switch ($size->getOperator()) {
|
||||||
|
case '<=':
|
||||||
$command->add('-size -' . ($size->getTarget() + 1) . 'c');
|
$command->add('-size -' . ($size->getTarget() + 1) . 'c');
|
||||||
continue;
|
break;
|
||||||
}
|
case '>=':
|
||||||
|
|
||||||
if ('<' === $size->getOperator()) {
|
|
||||||
$command->add('-size -'.$size->getTarget().'c');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('>=' === $size->getOperator()) {
|
|
||||||
$command->add('-size +'. ($size->getTarget() - 1) . 'c');
|
$command->add('-size +'. ($size->getTarget() - 1) . 'c');
|
||||||
continue;
|
break;
|
||||||
}
|
case '>':
|
||||||
|
|
||||||
if ('>' === $size->getOperator()) {
|
|
||||||
$command->add('-size +' . $size->getTarget() . 'c');
|
$command->add('-size +' . $size->getTarget() . 'c');
|
||||||
continue;
|
break;
|
||||||
}
|
case '!=':
|
||||||
|
|
||||||
if ('!=' === $size->getOperator()) {
|
|
||||||
$command->add('-size -' . $size->getTarget() . 'c');
|
$command->add('-size -' . $size->getTarget() . 'c');
|
||||||
$command->add('-size +' . $size->getTarget() . 'c');
|
$command->add('-size +' . $size->getTarget() . 'c');
|
||||||
continue;
|
case '<':
|
||||||
|
default:
|
||||||
|
$command->add('-size -' . $size->getTarget() . 'c');
|
||||||
}
|
}
|
||||||
|
|
||||||
$command->add('-size '.$size->getTarget().'c');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,39 +266,30 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('<=' === $date->getOperator()) {
|
switch ($date->getOperator()) {
|
||||||
|
case '<=':
|
||||||
$command->add('-mmin +' . ($mins - 1));
|
$command->add('-mmin +' . ($mins - 1));
|
||||||
continue;
|
break;
|
||||||
}
|
case '>=':
|
||||||
|
|
||||||
if ('<' === $date->getOperator()) {
|
|
||||||
$command->add('-mmin +'.$mins);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('>=' === $date->getOperator()) {
|
|
||||||
$command->add('-mmin -' . ($mins + 1));
|
$command->add('-mmin -' . ($mins + 1));
|
||||||
continue;
|
break;
|
||||||
}
|
case '>':
|
||||||
|
|
||||||
if ('>' === $date->getOperator()) {
|
|
||||||
$command->add('-mmin -' . $mins);
|
$command->add('-mmin -' . $mins);
|
||||||
continue;
|
break;
|
||||||
}
|
case '!=':
|
||||||
|
|
||||||
if ('!=' === $date->getOperator()) {
|
|
||||||
$command->add('-mmin +' . $mins.' -or -mmin -' . $mins);
|
$command->add('-mmin +' . $mins.' -or -mmin -' . $mins);
|
||||||
continue;
|
break;
|
||||||
|
case '<':
|
||||||
|
default:
|
||||||
|
$command->add('-mmin +' . $mins);
|
||||||
}
|
}
|
||||||
|
|
||||||
$command->add('-mmin '.$mins);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Command $command
|
* @param Command $command
|
||||||
* @param array $contains
|
* @param array $contains
|
||||||
* @param bool $not
|
* @param Boolean $not
|
||||||
*/
|
*/
|
||||||
private function buildContentFiltering(Command $command, array $contains, $not = false)
|
private function buildContentFiltering(Command $command, array $contains, $not = false)
|
||||||
{
|
{
|
||||||
|
@ -323,8 +306,9 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Symfony\Component\Finder\Shell\Command $command
|
* @param Command $command
|
||||||
* @param string $sort
|
* @param string $sort
|
||||||
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
private function buildSorting(Command $command, $sort)
|
private function buildSorting(Command $command, $sort)
|
||||||
|
@ -332,6 +316,7 @@ abstract class AbstractFindAdapter extends AbstractAdapter
|
||||||
switch ($sort) {
|
switch ($sort) {
|
||||||
case SortableIterator::SORT_BY_NAME:
|
case SortableIterator::SORT_BY_NAME:
|
||||||
$command->ins('sort')->add('| sort');
|
$command->ins('sort')->add('| sort');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
case SortableIterator::SORT_BY_TYPE:
|
case SortableIterator::SORT_BY_TYPE:
|
||||||
$format = '%y';
|
$format = '%y';
|
||||||
|
|
|
@ -17,14 +17,14 @@ namespace Symfony\Component\Finder\Adapter;
|
||||||
interface AdapterInterface
|
interface AdapterInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param bool $followLinks
|
* @param Boolean $followLinks
|
||||||
*
|
*
|
||||||
* @return AdapterInterface Current instance
|
* @return AdapterInterface Current instance
|
||||||
*/
|
*/
|
||||||
public function setFollowLinks($followLinks);
|
public function setFollowLinks($followLinks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $mode
|
* @param integer $mode
|
||||||
*
|
*
|
||||||
* @return AdapterInterface Current instance
|
* @return AdapterInterface Current instance
|
||||||
*/
|
*/
|
||||||
|
@ -94,14 +94,14 @@ interface AdapterInterface
|
||||||
public function setFilters(array $filters);
|
public function setFilters(array $filters);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Closure|int $sort
|
* @param \Closure|integer $sort
|
||||||
*
|
*
|
||||||
* @return AdapterInterface Current instance
|
* @return AdapterInterface Current instance
|
||||||
*/
|
*/
|
||||||
public function setSort($sort);
|
public function setSort($sort);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $path
|
* @param array $paths
|
||||||
*
|
*
|
||||||
* @return AdapterInterface Current instance
|
* @return AdapterInterface Current instance
|
||||||
*/
|
*/
|
||||||
|
@ -124,7 +124,7 @@ interface AdapterInterface
|
||||||
/**
|
/**
|
||||||
* Tests adapter support for current platform.
|
* Tests adapter support for current platform.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
public function isSupported();
|
public function isSupported();
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
namespace Symfony\Component\Finder\Adapter;
|
namespace Symfony\Component\Finder\Adapter;
|
||||||
|
|
||||||
use Symfony\Component\Finder\Iterator;
|
|
||||||
use Symfony\Component\Finder\Shell\Shell;
|
use Symfony\Component\Finder\Shell\Shell;
|
||||||
use Symfony\Component\Finder\Shell\Command;
|
use Symfony\Component\Finder\Shell\Command;
|
||||||
|
|
||||||
|
@ -22,14 +21,6 @@ use Symfony\Component\Finder\Shell\Command;
|
||||||
*/
|
*/
|
||||||
class BsdFindAdapter extends AbstractFindAdapter
|
class BsdFindAdapter extends AbstractFindAdapter
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function isSupported()
|
|
||||||
{
|
|
||||||
return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::isSupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -38,12 +29,25 @@ class BsdFindAdapter extends AbstractFindAdapter
|
||||||
return 'bsd_find';
|
return 'bsd_find';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function canBeUsed()
|
||||||
|
{
|
||||||
|
return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
protected function buildFormatSorting(Command $command, $format)
|
protected function buildFormatSorting(Command $command, $format)
|
||||||
{
|
{
|
||||||
$command->get('find')->add('-print0 | xargs -0 stat -f')->arg($format.' %h/%f\\n')
|
$command
|
||||||
->add('| sort | cut')->arg('-d ')->arg('-f2-');
|
->get('find')
|
||||||
|
->add('-print0 | xargs -0 stat -f')
|
||||||
|
->arg($format.' %h/%f\\n')
|
||||||
|
->add('| sort | cut')
|
||||||
|
->arg('-d ')
|
||||||
|
->arg('-f2-');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
namespace Symfony\Component\Finder\Adapter;
|
namespace Symfony\Component\Finder\Adapter;
|
||||||
|
|
||||||
use Symfony\Component\Finder\Iterator;
|
|
||||||
use Symfony\Component\Finder\Shell\Shell;
|
use Symfony\Component\Finder\Shell\Shell;
|
||||||
use Symfony\Component\Finder\Shell\Command;
|
use Symfony\Component\Finder\Shell\Command;
|
||||||
|
|
||||||
|
@ -22,14 +21,6 @@ use Symfony\Component\Finder\Shell\Command;
|
||||||
*/
|
*/
|
||||||
class GnuFindAdapter extends AbstractFindAdapter
|
class GnuFindAdapter extends AbstractFindAdapter
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function isSupported()
|
|
||||||
{
|
|
||||||
return $this->shell->getType() === Shell::TYPE_UNIX && parent::isSupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -43,8 +34,22 @@ class GnuFindAdapter extends AbstractFindAdapter
|
||||||
*/
|
*/
|
||||||
protected function buildFormatSorting(Command $command, $format)
|
protected function buildFormatSorting(Command $command, $format)
|
||||||
{
|
{
|
||||||
$command->get('find')->add('-printf')->arg($format.' %h/%f\\n')
|
$command
|
||||||
->add('| sort | cut')->arg('-d ')->arg('-f2-');
|
->get('find')
|
||||||
|
->add('-printf')
|
||||||
|
->arg($format.' %h/%f\\n')
|
||||||
|
->add('| sort | cut')
|
||||||
|
->arg('-d ')
|
||||||
|
->arg('-f2-')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function canBeUsed()
|
||||||
|
{
|
||||||
|
return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue