Merge branch '2.7' into 2.8

* 2.7: (28 commits)
  [Process] Use stream based storage to avoid memory issues
  Fix upgrade guides concerning erroneous removal of assets helper
  [Process] Remove a misleading comment
  Fix markdown typo
  ChooseBaseUrl should return an index
  [Form] ChoiceType: Fix a notice when 'choices' normalizer is replaced
  Improve the phpdoc of SplFileInfo methods
  [Process] Use stream based storage to avoid memory issues
  [FrameworkBundle] Don't log twice with the error handler
  Remove useless is_object condition
  [Process] Fix typo, no arguments needed anymore
  [Serializer] Introduce constants for context keys
  Fixed the documentation of VoterInterface::supportsAttribute
  Fixed Bootstrap form theme form "reset" buttons
  Remove useless duplicated tests
  [FrameworkBundle] Optimize framework extension tests
  synchronize 2.7 and 3.0 upgrade files
  fix merge 2.3 into 2.7 for SecureRandom dependency
  Use is_subclass_of instead of reflection
  Use is_subclass_of instead of Reflection when possible
  ...
This commit is contained in:
Fabien Potencier 2016-01-20 13:09:07 +01:00
commit fd50be95d9
32 changed files with 332 additions and 212 deletions

View File

@ -596,11 +596,11 @@ TwigBundle
FrameworkBundle
---------------
* The `templating.helper.assets` was refactored and returns now an object of the type
* The `templating.helper.assets` service was refactored and now returns an object of type
`Symfony\Bundle\FrameworkBundle\Templating\Helper\AssetsHelper` instead of
`Symfony\Component\Templating\Helper\CoreAssetsHelper`. You can update your class definition
or use the `assets.packages` service instead. Using the `assets.packages` service is the recommended
way. The `templating.helper.assets` service will be removed in Symfony 3.0.
way.
Before:

View File

@ -27,6 +27,9 @@ UPGRADE FROM 2.x to 3.0
work because the `\Symfony\Component\Config\Resource\BCResourceInterfaceChecker` helper class
has been removed as well.
* The `__toString()` method of the `\Symfony\Component\Config\ConfigCache` class
was removed in favor of the new `getPath()` method.
### Console
* The `dialog` helper has been removed in favor of the `question` helper.
@ -179,6 +182,26 @@ UPGRADE FROM 2.x to 3.0
removed: `ContainerBuilder::synchronize()`, `Definition::isSynchronized()`,
and `Definition::setSynchronized()`.
### DoctrineBridge
* The `property` option of `DoctrineType` was removed in favor of the `choice_label` option.
* The `loader` option of `DoctrineType` was removed. You now have to override the `getLoader()`
method in your custom type.
* The `Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList` was removed in favor
of `Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader`.
* Passing a query builder closure to `ORMQueryBuilderLoader` is not supported anymore.
You should pass resolved query builders only.
Consequently, the arguments `$manager` and `$class` of `ORMQueryBuilderLoader`
have been removed as well.
Note that the `query_builder` option of `DoctrineType` *does* support
closures, but the closure is now resolved in the type instead of in the
loader.
### EventDispatcher
* The interface `Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface`
@ -342,6 +365,11 @@ UPGRADE FROM 2.x to 3.0
</service>
```
* The `ChoiceToBooleanArrayTransformer`, `ChoicesToBooleanArrayTransformer`,
`FixRadioInputListener`, and `FixCheckboxInputListener` classes were removed.
* The `choice_list` option of `ChoiceType` was removed.
* The option "precision" was renamed to "scale".
Before:
@ -521,11 +549,11 @@ UPGRADE FROM 2.x to 3.0
`NumberToLocalizedStringTransformer` were renamed to `ROUND_HALF_EVEN`,
`ROUND_HALF_UP` and `ROUND_HALF_DOWN`.
* The methods `ChoiceListInterface::getIndicesForChoices()` and
`ChoiceListInterface::getIndicesForValues()` were removed. No direct
replacement exists, although in most cases
`ChoiceListInterface::getChoicesForValues()` and
`ChoiceListInterface::getValuesForChoices()` should be sufficient.
* The `Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface` was
removed in favor of `Symfony\Component\Form\ChoiceList\ChoiceListInterface`.
* `Symfony\Component\Form\Extension\Core\View\ChoiceView` was removed in favor of
`Symfony\Component\Form\ChoiceList\View\ChoiceView`.
* The interface `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface`
and all of its implementations were removed. Use the new interface
@ -649,51 +677,6 @@ UPGRADE FROM 2.x to 3.0
* The `request` service was removed. You must inject the `request_stack`
service instead.
* The `templating.helper.assets` was removed in Symfony 3.0. You should
use the `assets.package` service instead.
Before:
```php
use Symfony\Component\Templating\Helper\CoreAssetsHelper;
class DemoService
{
private $assetsHelper;
public function __construct(CoreAssetsHelper $assetsHelper)
{
$this->assetsHelper = $assetsHelper;
}
public function testMethod()
{
return $this->assetsHelper->getUrl('thumbnail.png', null, $this->assetsHelper->getVersion());
}
}
```
After:
```php
use Symfony\Component\Asset\Packages;
class DemoService
{
private $assetPackages;
public function __construct(Packages $assetPackages)
{
$this->assetPackages = $assetPackages;
}
public function testMethod()
{
return $this->assetPackages->getUrl('thumbnail.png').$this->assetPackages->getVersion();
}
}
```
* The `enctype` method of the `form` helper was removed. You should use the
new method `start` instead.
@ -836,7 +819,7 @@ UPGRADE FROM 2.x to 3.0
* Some route settings have been renamed:
* The `pattern` setting for a route has been deprecated in favor of `path`
* The `pattern` setting has been removed in favor of `path`
* The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings
Before:
@ -889,6 +872,9 @@ UPGRADE FROM 2.x to 3.0
the performance gains were minimal and it's hard to replicate the behaviour
of PHP implementation.
* The `getMatcherDumperInstance()` and `getGeneratorDumperInstance()` methods in the
`Symfony\Component\Routing\Router` have been changed from `public` to `protected`.
### Security
* The `AbstractVoter` class was removed in favor of the new `Voter` class.
@ -1023,6 +1009,16 @@ UPGRADE FROM 2.x to 3.0
}
```
### Serializer
* The `setCamelizedAttributes()` method of the
`Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer` and
`Symfony\Component\Serializer\Normalizer\PropertyNormalizer` classes
was removed.
* The `Symfony\Component\Serializer\Exception\Exception` interface was removed
in favor of the new `Symfony\Component\Serializer\Exception\ExceptionInterface`.
### Translator
* The `Translator::setFallbackLocale()` method has been removed in favor of
@ -1091,11 +1087,18 @@ UPGRADE FROM 2.x to 3.0
### TwigBundle
* The `Symfony\Bundle\TwigBundle\TwigDefaultEscapingStrategy` was removed
in favor of `Twig_FileExtensionEscapingStrategy`.
* The `twig:debug` command has been deprecated since Symfony 2.7 and will be
removed in Symfony 3.0. Use the `debug:twig` command instead.
### Validator
* The PHP7-incompatible constraints (`Null`, `True`, `False`) and their related
validators (`NullValidator`, `TrueValidator`, `FalseValidator`) have been
removed in favor of their `Is`-prefixed equivalent.
* The class `Symfony\Component\Validator\Mapping\Cache\ApcCache` has been removed in favor
of `Symfony\Component\Validator\Mapping\Cache\DoctrineCache`.

View File

@ -49,12 +49,8 @@ class DbalLogger implements SQLLogger
$this->stopwatch->start('doctrine', 'doctrine');
}
if (is_array($params)) {
$params = $this->normalizeParams($params);
}
if (null !== $this->logger) {
$this->log($sql, null === $params ? array() : $params);
$this->log($sql, null === $params ? array() : $this->normalizeParams($params));
}
}

View File

@ -65,6 +65,17 @@ col-sm-2
{% endspaceless %}
{% endblock submit_row %}
{% block reset_row -%}
{% spaceless %}
<div class="form-group">
<div class="{{ block('form_label_class') }}"></div>
<div class="{{ block('form_group_class') }}">
{{ form_widget(form) }}
</div>
</div>
{% endspaceless %}
{% endblock reset_row %}
{% block form_group_class -%}
col-sm-10
{%- endblock form_group_class %}

View File

@ -167,7 +167,14 @@
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %}
{% endif %}
{% if label is not same as(false) and label is empty %}
{% set label = name|humanize %}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{% endif %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
{{- widget|raw }} {{ label is not same as(false) ? (translation_domain is same as(false) ? label : label|trans({}, translation_domain)) -}}

View File

@ -37,8 +37,7 @@ class AddConsoleCommandPass implements CompilerPassInterface
}
$class = $container->getParameterBag()->resolveValue($definition->getClass());
$r = new \ReflectionClass($class);
if (!$r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command')) {
if (!is_subclass_of($class, 'Symfony\\Component\\Console\\Command\\Command')) {
throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be a subclass of "Symfony\\Component\\Console\\Command\\Command".', $id));
}
$container->setAlias('console.command.'.strtolower(str_replace('\\', '_', $class)), $id);

View File

@ -9,11 +9,8 @@
</parameters>
<services>
<!-- Pseudo-Random Number Generator -->
<service id="security.secure_random" class="%security.secure_random.class%">
<tag name="monolog.logger" channel="security" />
<argument>%kernel.cache_dir%/secure_random.seed</argument>
<argument type="service" id="logger" on-invalid="ignore" />
<!-- Pseudorandom Number Generator -->
<service id="security.secure_random" class="Symfony\Component\Security\Core\Util\SecureRandom">
<deprecated>The "%service_id%" service is deprecated since Symfony 2.8 and will be removed in 3.0. Use the random_bytes() function instead.</deprecated>
</service>
</services>

View File

@ -22,6 +22,8 @@ use Symfony\Component\Validator\Validation;
abstract class FrameworkExtensionTest extends TestCase
{
private static $containerCache = array();
abstract protected function loadFromFile(ContainerBuilder $container, $file);
public function testCsrfProtection()
@ -511,6 +513,10 @@ abstract class FrameworkExtensionTest extends TestCase
protected function createContainerFromFile($file, $data = array())
{
$cacheKey = md5($file.serialize($data));
if (isset(self::$containerCache[$cacheKey])) {
return self::$containerCache[$cacheKey];
}
$container = $this->createContainer($data);
$container->registerExtension(new FrameworkExtension());
$this->loadFromFile($container, $file);
@ -519,7 +525,7 @@ abstract class FrameworkExtensionTest extends TestCase
$container->getCompilerPassConfig()->setRemovingPasses(array());
$container->compile();
return $container;
return self::$containerCache[$cacheKey] = $container;
}
protected function createContainerFromClosure($closure, $data = array())

View File

@ -60,6 +60,11 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
$files = $this->extractFiles($resource);
foreach ($files as $file) {
$this->parseTokens(token_get_all(file_get_contents($file)), $catalog);
if (PHP_VERSION_ID >= 70000) {
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
gc_mem_caches();
}
}
}
@ -80,7 +85,7 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
*/
protected function normalizeToken($token)
{
if (is_array($token)) {
if (isset($token[1]) && 'b"' !== $token) {
return $token[1];
}
@ -94,7 +99,7 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
{
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if (!is_array($t) || ($t[0] !== T_WHITESPACE)) {
if (T_WHITESPACE !== $t[0]) {
break;
}
}
@ -111,7 +116,7 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if (!is_array($t)) {
if (!isset($t[1])) {
break;
}

View File

@ -28,7 +28,7 @@
"symfony/polyfill-mbstring": "~1.0",
"symfony/filesystem": "~2.3|~3.0.0",
"symfony/routing": "~2.8|~3.0.0",
"symfony/security-core": "~2.6|~3.0.0",
"symfony/security-core": "~2.6.13|~2.7.9|~2.8|~3.0.0",
"symfony/security-csrf": "~2.6|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0",
"symfony/templating": "~2.1|~3.0.0",

View File

@ -108,15 +108,15 @@ class UrlPackage extends Package
* Determines which base URL to use for the given path.
*
* Override this method to change the default distribution strategy.
* This method should always return the same base URL for a given path.
* This method should always return the same base URL index for a given path.
*
* @param string $path
*
* @return string The base URL for the given path
* @return int The base URL index for the given path
*/
protected function chooseBaseUrl($path)
{
return fmod(hexdec(substr(hash('sha256', $path), 0, 10)), count($this->baseUrls));
return (int) fmod(hexdec(substr(hash('sha256', $path), 0, 10)), count($this->baseUrls));
}
private function getSslUrls($urls)

View File

@ -149,8 +149,9 @@ class ClassCollectionLoader
$inNamespace = false;
$tokens = token_get_all($source);
for (reset($tokens); false !== $token = current($tokens); next($tokens)) {
if (is_string($token)) {
for ($i = 0; isset($tokens[$i]); ++$i) {
$token = $tokens[$i];
if (!isset($token[1]) || 'b"' === $token) {
$rawChunk .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
// strip comments
@ -162,12 +163,12 @@ class ClassCollectionLoader
$rawChunk .= $token[1];
// namespace name and whitespaces
while (($t = next($tokens)) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
$rawChunk .= $t[1];
while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
$rawChunk .= $tokens[$i][1];
}
if ('{' === $t) {
if ('{' === $tokens[$i]) {
$inNamespace = false;
prev($tokens);
--$i;
} else {
$rawChunk = rtrim($rawChunk)."\n{";
$inNamespace = true;
@ -175,8 +176,8 @@ class ClassCollectionLoader
} elseif (T_START_HEREDOC === $token[0]) {
$output .= self::compressCode($rawChunk).$token[1];
do {
$token = next($tokens);
$output .= is_string($token) ? $token : $token[1];
$token = $tokens[++$i];
$output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
} while ($token[0] !== T_END_HEREDOC);
$output .= "\n";
$rawChunk = '';
@ -192,7 +193,15 @@ class ClassCollectionLoader
$rawChunk .= "}\n";
}
return $output.self::compressCode($rawChunk);
$output .= self::compressCode($rawChunk);
if (PHP_VERSION_ID >= 70000) {
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
unset($tokens, $rawChunk);
gc_mem_caches();
}
return $output;
}
/**

View File

@ -72,6 +72,11 @@ class ClassMapGenerator
$classes = self::findClasses($path);
if (PHP_VERSION_ID >= 70000) {
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
gc_mem_caches();
}
foreach ($classes as $class) {
$map[$class] = $path;
}
@ -95,10 +100,10 @@ class ClassMapGenerator
$classes = array();
$namespace = '';
for ($i = 0, $max = count($tokens); $i < $max; ++$i) {
for ($i = 0; isset($tokens[$i]); ++$i) {
$token = $tokens[$i];
if (is_string($token)) {
if (!isset($token[1])) {
continue;
}
@ -108,9 +113,9 @@ class ClassMapGenerator
case T_NAMESPACE:
$namespace = '';
// If there is a namespace, extract it
while (($t = $tokens[++$i]) && is_array($t)) {
if (in_array($t[0], array(T_STRING, T_NS_SEPARATOR))) {
$namespace .= $t[1];
while (isset($tokens[++$i][1])) {
if (in_array($tokens[$i][0], array(T_STRING, T_NS_SEPARATOR))) {
$namespace .= $tokens[$i][1];
}
}
$namespace .= '\\';
@ -121,7 +126,7 @@ class ClassMapGenerator
// Skip usage of ::class constant
$isClassConstant = false;
for ($j = $i - 1; $j > 0; --$j) {
if (is_string($tokens[$j])) {
if (!isset($tokens[$j][1])) {
break;
}
@ -138,10 +143,11 @@ class ClassMapGenerator
}
// Find the classname
while (($t = $tokens[++$i]) && is_array($t)) {
while (isset($tokens[++$i][1])) {
$t = $tokens[$i];
if (T_STRING === $t[0]) {
$class .= $t[1];
} elseif ($class !== '' && T_WHITESPACE == $t[0]) {
} elseif ('' !== $class && T_WHITESPACE === $t[0]) {
break;
}
}

View File

@ -38,6 +38,8 @@ class SplFileInfo extends \SplFileInfo
/**
* Returns the relative path.
*
* This path does not contain the file name.
*
* @return string the relative path
*/
public function getRelativePath()
@ -48,6 +50,8 @@ class SplFileInfo extends \SplFileInfo
/**
* Returns the relative path name.
*
* This path contains the file name.
*
* @return string the relative path name
*/
public function getRelativePathname()

View File

@ -291,6 +291,10 @@ class ChoiceType extends AbstractType
// forms)
$labels = $choiceLabels->labels;
// The $choiceLabels object is shared with the 'choices' closure.
// Since that normalizer can be replaced, labels have to be cleared here.
$choiceLabels->labels = array();
return function ($choice, $key) use ($labels) {
return $labels[$key];
};

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
use Symfony\Component\Form\Tests\Fixtures\ChoiceSubType;
class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
{
@ -1902,4 +1903,30 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
// Trigger data initialization
$form->getViewData();
}
/**
* This covers the case when:
* - Custom choice type added after a choice type.
* - Custom type is expanded.
* - Custom type replaces 'choices' normalizer with a custom one.
* In this case, custom type should not inherit labels from the first added choice type.
*/
public function testCustomChoiceTypeDoesNotInheritChoiceLabels()
{
$builder = $this->factory->createBuilder();
$builder->add('choice', 'choice', array(
'choices' => array(
'1' => '1',
'2' => '2',
),
)
);
$builder->add('subChoice', new ChoiceSubType());
$form = $builder->getForm();
// The default 'choices' normalizer would fill the $choiceLabels, but it has been replaced
// in the custom choice type, so $choiceLabels->labels remains empty array.
// In this case the 'choice_label' closure returns null and not the closure from the first choice type.
$this->assertNull($form->get('subChoice')->getConfig()->getOption('choice_label'));
}
}

View File

@ -0,0 +1,51 @@
<?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\Form\Tests\Fixtures;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @author Paráda József <joczy.parada@gmail.com>
*/
class ChoiceSubType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('expanded' => true));
$resolver->setNormalizer('choices', function () {
return array(
'attr1' => 'Attribute 1',
'attr2' => 'Attribute 2',
);
});
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'sub_choice';
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'choice';
}
}

View File

@ -17,11 +17,6 @@ use Symfony\Component\HttpFoundation\Request;
class RequestTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$this->testInitialize();
}
public function testInitialize()
{
$request = new Request();

View File

@ -713,14 +713,15 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$output = '';
$tokens = token_get_all($source);
$ignoreSpace = false;
for (reset($tokens); false !== $token = current($tokens); next($tokens)) {
if (is_string($token)) {
for ($i = 0; isset($tokens[$i]); ++$i) {
$token = $tokens[$i];
if (!isset($token[1]) || 'b"' === $token) {
$rawChunk .= $token;
} elseif (T_START_HEREDOC === $token[0]) {
$output .= $rawChunk.$token[1];
do {
$token = next($tokens);
$output .= $token[1];
$token = $tokens[++$i];
$output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
} while ($token[0] !== T_END_HEREDOC);
$rawChunk = '';
} elseif (T_WHITESPACE === $token[0]) {
@ -746,6 +747,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$output .= $rawChunk;
if (PHP_VERSION_ID >= 70000) {
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
unset($tokens, $rawChunk);
gc_mem_caches();
}
return $output;
}

View File

@ -246,7 +246,7 @@ modified';
$heredoc = <<<HD
Heredoc should not be modified
Heredoc should not be modified {$a[1+$b]}
HD;
@ -282,7 +282,7 @@ modified';
$heredoc = <<<HD
Heredoc should not be modified
Heredoc should not be modified {$a[1+$b]}
HD;

View File

@ -240,9 +240,6 @@ class Process
* The callback receives the type of output (out or err) and some bytes from
* the output in real-time while writing the standard input to the process.
* It allows to have feedback from the independent process during execution.
* If there is no callback passed, the wait() method can be called
* with true as a second parameter then the callback will get all data occurred
* in (and since) the start call.
*
* @param callable|null $callback A PHP callback to run whenever there is some
* output available on STDOUT or STDERR
@ -474,7 +471,11 @@ class Process
$this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
return $this->stdout;
if (false === $ret = stream_get_contents($this->stdout, -1, 0)) {
return '';
}
return $ret;
}
/**
@ -490,18 +491,19 @@ class Process
*/
public function getIncrementalOutput()
{
if ($this->outputDisabled) {
throw new LogicException('Output has been disabled.');
}
$this->requireProcessIsStarted(__FUNCTION__);
$data = $this->getOutput();
$latest = substr($data, $this->incrementalOutputOffset);
$latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset);
$this->incrementalOutputOffset = ftell($this->stdout);
if (false === $latest) {
return '';
}
$this->incrementalOutputOffset = strlen($data);
return $latest;
}
@ -512,7 +514,8 @@ class Process
*/
public function clearOutput()
{
$this->stdout = '';
ftruncate($this->stdout, 0);
fseek($this->stdout, 0);
$this->incrementalOutputOffset = 0;
return $this;
@ -536,7 +539,11 @@ class Process
$this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true);
return $this->stderr;
if (false === $ret = stream_get_contents($this->stderr, -1, 0)) {
return '';
}
return $ret;
}
/**
@ -553,18 +560,19 @@ class Process
*/
public function getIncrementalErrorOutput()
{
if ($this->outputDisabled) {
throw new LogicException('Output has been disabled.');
}
$this->requireProcessIsStarted(__FUNCTION__);
$data = $this->getErrorOutput();
$latest = substr($data, $this->incrementalErrorOutputOffset);
$latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset);
$this->incrementalErrorOutputOffset = ftell($this->stderr);
if (false === $latest) {
return '';
}
$this->incrementalErrorOutputOffset = strlen($data);
return $latest;
}
@ -575,7 +583,8 @@ class Process
*/
public function clearErrorOutput()
{
$this->stderr = '';
ftruncate($this->stderr, 0);
fseek($this->stderr, 0);
$this->incrementalErrorOutputOffset = 0;
return $this;
@ -795,23 +804,33 @@ class Process
/**
* Adds a line to the STDOUT stream.
*
* @internal
*
* @param string $line The line to append
*/
public function addOutput($line)
{
$this->lastOutputTime = microtime(true);
$this->stdout .= $line;
fseek($this->stdout, 0, SEEK_END);
fwrite($this->stdout, $line);
fseek($this->stdout, $this->incrementalOutputOffset);
}
/**
* Adds a line to the STDERR stream.
*
* @internal
*
* @param string $line The line to append
*/
public function addErrorOutput($line)
{
$this->lastOutputTime = microtime(true);
$this->stderr .= $line;
fseek($this->stderr, 0, SEEK_END);
fwrite($this->stderr, $line);
fseek($this->stderr, $this->incrementalErrorOutputOffset);
}
/**
@ -1232,7 +1251,7 @@ class Process
$this->processPipes = UnixPipes::create($this, $this->input);
}
return $this->processPipes->getDescriptors($this->outputDisabled);
return $this->processPipes->getDescriptors();
}
/**
@ -1393,8 +1412,8 @@ class Process
$this->exitcode = null;
$this->fallbackStatus = array();
$this->processInformation = null;
$this->stdout = null;
$this->stderr = null;
$this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'wb+');
$this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'wb+');
$this->process = null;
$this->latestSignal = null;
$this->status = self::STATUS_READY;

View File

@ -110,7 +110,7 @@ abstract class AnnotationClassLoader implements LoaderInterface
$class = new \ReflectionClass($class);
if ($class->isAbstract()) {
throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class));
throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class->getName()));
}
$globals = $this->getGlobals($class);

View File

@ -64,6 +64,10 @@ class AnnotationFileLoader extends FileLoader
$collection->addResource(new FileResource($path));
$collection->addCollection($this->loader->load($class, $type));
}
if (PHP_VERSION_ID >= 70000) {
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
gc_mem_caches();
}
return $collection;
}
@ -88,10 +92,10 @@ class AnnotationFileLoader extends FileLoader
$class = false;
$namespace = false;
$tokens = token_get_all(file_get_contents($file));
for ($i = 0, $count = count($tokens); $i < $count; ++$i) {
for ($i = 0; isset($tokens[$i]); ++$i) {
$token = $tokens[$i];
if (!is_array($token)) {
if (!isset($token[1])) {
continue;
}
@ -100,11 +104,11 @@ class AnnotationFileLoader extends FileLoader
}
if (true === $namespace && T_STRING === $token[0]) {
$namespace = '';
do {
$namespace .= $token[1];
$token = $tokens[++$i];
} while ($i < $count && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
$namespace = $token[1];
while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_NS_SEPARATOR, T_STRING))) {
$namespace .= $tokens[$i][1];
}
$token = $tokens[$i];
}
if (T_CLASS === $token[0]) {

View File

@ -27,7 +27,7 @@ interface VoterInterface
/**
* Checks if the voter supports the given attribute.
*
* @param string $attribute An attribute
* @param mixed $attribute An attribute (usually the attribute name string)
*
* @return bool true if this Voter supports the attribute, false otherwise
*

View File

@ -27,6 +27,10 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
*/
abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit';
const OBJECT_TO_POPULATE = 'object_to_populate';
const GROUPS = 'groups';
/**
* @var int
*/
@ -185,16 +189,16 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
{
$objectHash = spl_object_hash($object);
if (isset($context['circular_reference_limit'][$objectHash])) {
if ($context['circular_reference_limit'][$objectHash] >= $this->circularReferenceLimit) {
unset($context['circular_reference_limit'][$objectHash]);
if (isset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash])) {
if ($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] >= $this->circularReferenceLimit) {
unset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash]);
return true;
}
++$context['circular_reference_limit'][$objectHash];
++$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash];
} else {
$context['circular_reference_limit'][$objectHash] = 1;
$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] = 1;
}
return false;
@ -248,13 +252,13 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
*/
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
{
if (!$this->classMetadataFactory || !isset($context['groups']) || !is_array($context['groups'])) {
if (!$this->classMetadataFactory || !isset($context[static::GROUPS]) || !is_array($context[static::GROUPS])) {
return false;
}
$allowedAttributes = array();
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) {
if (count(array_intersect($attributeMetadata->getGroups(), $context[static::GROUPS]))) {
$allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata;
}
}
@ -296,12 +300,12 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
{
if (
isset($context['object_to_populate']) &&
is_object($context['object_to_populate']) &&
$context['object_to_populate'] instanceof $class
isset($context[static::OBJECT_TO_POPULATE]) &&
is_object($context[static::OBJECT_TO_POPULATE]) &&
$context[static::OBJECT_TO_POPULATE] instanceof $class
) {
$object = $context['object_to_populate'];
unset($context['object_to_populate']);
$object = $context[static::OBJECT_TO_POPULATE];
unset($context[static::OBJECT_TO_POPULATE]);
return $object;
}

View File

@ -63,8 +63,6 @@ class CustomNormalizer extends SerializerAwareNormalizer implements NormalizerIn
return false;
}
$class = new \ReflectionClass($type);
return $class->isSubclassOf('Symfony\Component\Serializer\Normalizer\DenormalizableInterface');
return is_subclass_of($type, 'Symfony\Component\Serializer\Normalizer\DenormalizableInterface');
}
}

View File

@ -127,10 +127,8 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
if (null === $data || is_scalar($data)) {
return $data;
}
if (is_object($data) && $this->supportsNormalization($data, $format)) {
return $this->normalizeObject($data, $format, $context);
}
if ($data instanceof \Traversable) {
if (is_array($data) || $data instanceof \Traversable) {
$normalized = array();
foreach ($data as $key => $val) {
$normalized[$key] = $this->normalize($val, $format, $context);
@ -138,16 +136,15 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
return $normalized;
}
if (is_object($data)) {
return $this->normalizeObject($data, $format, $context);
}
if (is_array($data)) {
foreach ($data as $key => $val) {
$data[$key] = $this->normalize($val, $format, $context);
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
}
return $data;
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', get_class($data)));
}
throw new UnexpectedValueException(sprintf('An unexpected value could not be normalized: %s', var_export($data, true)));
}
@ -185,10 +182,6 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
private function getNormalizer($data, $format)
{
if ($isObject = is_object($data)) {
$class = get_class($data);
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format)) {
return $normalizer;
@ -230,31 +223,6 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
return $this->decoder->decode($data, $format, $context);
}
/**
* Normalizes an object into a set of arrays/scalars.
*
* @param object $object object to normalize
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @param array $context The context data for this particular normalization
*
* @return array|string|bool|int|float|null
*
* @throws LogicException
* @throws UnexpectedValueException
*/
private function normalizeObject($object, $format, array $context = array())
{
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
}
if ($normalizer = $this->getNormalizer($object, $format)) {
return $normalizer->normalize($object, $format, $context);
}
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', get_class($object)));
}
/**
* Denormalizes data back into an object of the given class.
*

View File

@ -5,6 +5,7 @@ namespace Symfony\Component\Serializer\Tests\Normalizer;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractNormalizerDummy;
use Symfony\Component\Serializer\Tests\Fixtures\ProxyDummy;
@ -55,10 +56,10 @@ class AbstractNormalizerTest extends \PHPUnit_Framework_TestCase
$this->classMetadata->method('getMetadataFor')->willReturn($classMetadata);
$result = $this->normalizer->getAllowedAttributes('c', array('groups' => array('test')), true);
$result = $this->normalizer->getAllowedAttributes('c', array(AbstractNormalizer::GROUPS => array('test')), true);
$this->assertEquals(array('a2', 'a4'), $result);
$result = $this->normalizer->getAllowedAttributes('c', array('groups' => array('other')), true);
$result = $this->normalizer->getAllowedAttributes('c', array(AbstractNormalizer::GROUPS => array('other')), true);
$this->assertEquals(array('a3', 'a4'), $result);
}
@ -84,10 +85,10 @@ class AbstractNormalizerTest extends \PHPUnit_Framework_TestCase
$this->classMetadata->method('getMetadataFor')->willReturn($classMetadata);
$result = $this->normalizer->getAllowedAttributes('c', array('groups' => array('test')), false);
$result = $this->normalizer->getAllowedAttributes('c', array(AbstractNormalizer::GROUPS => array('test')), false);
$this->assertEquals(array($a2, $a4), $result);
$result = $this->normalizer->getAllowedAttributes('c', array('groups' => array('other')), false);
$result = $this->normalizer->getAllowedAttributes('c', array(AbstractNormalizer::GROUPS => array('other')), false);
$this->assertEquals(array($a3, $a4), $result);
}
@ -95,7 +96,7 @@ class AbstractNormalizerTest extends \PHPUnit_Framework_TestCase
{
$proxyDummy = new ProxyDummy();
$context = array('object_to_populate' => $proxyDummy);
$context = array(AbstractNormalizer::OBJECT_TO_POPULATE => $proxyDummy);
$normalizer = new ObjectNormalizer();
$normalizer->denormalize(array('foo' => 'bar'), 'Symfony\Component\Serializer\Tests\Fixtures\ToBeProxyfiedDummy', null, $context);

View File

@ -277,7 +277,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(
'bar' => 'bar',
), $this->normalizer->normalize($obj, null, array('groups' => array('c'))));
), $this->normalizer->normalize($obj, null, array(GetSetMethodNormalizer::GROUPS => array('c'))));
$this->assertEquals(array(
'symfony' => 'symfony',
@ -286,7 +286,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
'bar' => 'bar',
'kevin' => 'kevin',
'coopTilleuls' => 'coopTilleuls',
), $this->normalizer->normalize($obj, null, array('groups' => array('a', 'c'))));
), $this->normalizer->normalize($obj, null, array(GetSetMethodNormalizer::GROUPS => array('a', 'c'))));
}
public function testGroupsDenormalize()
@ -304,7 +304,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$toNormalize,
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
null,
array('groups' => array('a'))
array(GetSetMethodNormalizer::GROUPS => array('a'))
);
$this->assertEquals($obj, $normalized);
@ -314,7 +314,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$toNormalize,
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
null,
array('groups' => array('a', 'b'))
array(GetSetMethodNormalizer::GROUPS => array('a', 'b'))
);
$this->assertEquals($obj, $normalized);
}
@ -336,7 +336,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
'foo_bar' => '@dunglas',
'symfony' => '@coopTilleuls',
),
$this->normalizer->normalize($obj, null, array('groups' => array('name_converter')))
$this->normalizer->normalize($obj, null, array(GetSetMethodNormalizer::GROUPS => array('name_converter')))
);
}
@ -357,7 +357,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
'foo_bar' => '@dunglas',
'symfony' => '@coopTilleuls',
'coop_tilleuls' => 'les-tilleuls.coop',
), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array('groups' => array('name_converter')))
), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array(GetSetMethodNormalizer::GROUPS => array('name_converter')))
);
}
@ -533,7 +533,7 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
array('bar' => 'bar'),
__NAMESPACE__.'\GetSetDummy',
null,
array('object_to_populate' => $dummy)
array(GetSetMethodNormalizer::OBJECT_TO_POPULATE => $dummy)
);
$this->assertEquals($dummy, $obj);

View File

@ -197,7 +197,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(
'bar' => 'bar',
), $this->normalizer->normalize($obj, null, array('groups' => array('c'))));
), $this->normalizer->normalize($obj, null, array(ObjectNormalizer::GROUPS => array('c'))));
$this->assertEquals(array(
'symfony' => 'symfony',
@ -206,7 +206,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
'bar' => 'bar',
'kevin' => 'kevin',
'coopTilleuls' => 'coopTilleuls',
), $this->normalizer->normalize($obj, null, array('groups' => array('a', 'c'))));
), $this->normalizer->normalize($obj, null, array(ObjectNormalizer::GROUPS => array('a', 'c'))));
}
public function testGroupsDenormalize()
@ -224,7 +224,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
$toNormalize,
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
null,
array('groups' => array('a'))
array(ObjectNormalizer::GROUPS => array('a'))
);
$this->assertEquals($obj, $normalized);
@ -234,7 +234,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
$toNormalize,
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
null,
array('groups' => array('a', 'b'))
array(ObjectNormalizer::GROUPS => array('a', 'b'))
);
$this->assertEquals($obj, $normalized);
}
@ -268,7 +268,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
'foo_bar' => '@dunglas',
'symfony' => '@coopTilleuls',
),
$this->normalizer->normalize($obj, null, array('groups' => array('name_converter')))
$this->normalizer->normalize($obj, null, array(ObjectNormalizer::GROUPS => array('name_converter')))
);
}
@ -289,7 +289,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
'foo_bar' => '@dunglas',
'symfony' => '@coopTilleuls',
'coop_tilleuls' => 'les-tilleuls.coop',
), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array('groups' => array('name_converter')))
), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array(ObjectNormalizer::GROUPS => array('name_converter')))
);
}

View File

@ -214,7 +214,7 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(
'bar' => 'bar',
), $this->normalizer->normalize($obj, null, array('groups' => array('c'))));
), $this->normalizer->normalize($obj, null, array(PropertyNormalizer::GROUPS => array('c'))));
// The PropertyNormalizer is not able to hydrate properties from parent classes
$this->assertEquals(array(
@ -222,7 +222,7 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
'foo' => 'foo',
'fooBar' => 'fooBar',
'bar' => 'bar',
), $this->normalizer->normalize($obj, null, array('groups' => array('a', 'c'))));
), $this->normalizer->normalize($obj, null, array(PropertyNormalizer::GROUPS => array('a', 'c'))));
}
public function testGroupsDenormalize()
@ -240,7 +240,7 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
$toNormalize,
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
null,
array('groups' => array('a'))
array(PropertyNormalizer::GROUPS => array('a'))
);
$this->assertEquals($obj, $normalized);
@ -250,7 +250,7 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
$toNormalize,
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
null,
array('groups' => array('a', 'b'))
array(PropertyNormalizer::GROUPS => array('a', 'b'))
);
$this->assertEquals($obj, $normalized);
}
@ -272,7 +272,7 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
'foo_bar' => '@dunglas',
'symfony' => '@coopTilleuls',
),
$this->normalizer->normalize($obj, null, array('groups' => array('name_converter')))
$this->normalizer->normalize($obj, null, array(PropertyNormalizer::GROUPS => array('name_converter')))
);
}
@ -293,7 +293,7 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase
'foo_bar' => '@dunglas',
'symfony' => '@coopTilleuls',
'coop_tilleuls' => 'les-tilleuls.coop',
), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array('groups' => array('name_converter')))
), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array(PropertyNormalizer::GROUPS => array('name_converter')))
);
}

View File

@ -99,7 +99,6 @@ class CardSchemeValidatorTest extends AbstractConstraintValidatorTest
array('MAESTRO', '5020507657408074712'),
array('MAESTRO', '5612559223580173965'),
array('MAESTRO', '6759744069209'),
array('MAESTRO', '6759744069209'),
array('MAESTRO', '6594371785970435599'),
array('MASTERCARD', '5555555555554444'),
array('MASTERCARD', '5105105105105100'),