Merge branch '4.4' into 5.0
* 4.4: (23 commits) [Filesystem] Handle paths on different drives [WebProfiler] Do not add src-elem CSP directives if they do not exist [Yaml] fix parse error when unindented collections contain a comment Execute docker dependent tests with github actions Update exception.html.php [3.4][Inflector] Improve testSingularize() argument name [Inflector] Fix testPluralize() arguments names [PhpUnitBridge] fix PHP 5.3 compat again Skip validation when email is an empty object fix sr_Latn translation [Validator] fix lazy property usage. Fix annotation [Debug][ErrorHandler] cleanup phpunit.xml.dist files [Translation] Fix for translation:update command updating ICU messages [PhpUnitBridge] fix compat with PHP 5.3 bumped Symfony version to 4.4.9 updated VERSION for 4.4.8 updated CHANGELOG for 4.4.8 provide a useful message when extension types don't match [Cache] Fixed not supported Redis eviction policies ...
This commit is contained in:
commit
ae226ee34b
|
@ -0,0 +1,101 @@
|
|||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
integration:
|
||||
name: Integration
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php: ['7.1', '7.4']
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis:6.0.0
|
||||
ports:
|
||||
- 6379:6379
|
||||
redis-cluster:
|
||||
image: grokzen/redis-cluster:5.0.4
|
||||
ports:
|
||||
- 7000:7000
|
||||
- 7001:7001
|
||||
- 7002:7002
|
||||
- 7003:7003
|
||||
- 7004:7004
|
||||
- 7005:7005
|
||||
- 7006:7006
|
||||
- 7007:7007
|
||||
env:
|
||||
STANDALONE: true
|
||||
memcached:
|
||||
image: memcached:1.6.5
|
||||
ports:
|
||||
- 11211:11211
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.8.3
|
||||
ports:
|
||||
- 5672:5672
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: "none"
|
||||
extensions: "memcached,redis,xsl"
|
||||
ini-values: "memory_limit=-1"
|
||||
php-version: "${{ matrix.php }}"
|
||||
tools: flex
|
||||
|
||||
- name: Configure composer
|
||||
run: |
|
||||
([ -d ~/.composer ] || mkdir ~/.composer) && cp .github/composer-config.json ~/.composer/config.json
|
||||
SYMFONY_VERSION=$(cat composer.json | grep '^ *\"dev-master\". *\"[1-9]' | grep -o '[0-9.]*')
|
||||
echo "::set-env name=SYMFONY_VERSION::$SYMFONY_VERSION"
|
||||
echo "::set-env name=COMPOSER_ROOT_VERSION::$SYMFONY_VERSION.x-dev"
|
||||
|
||||
- name: Determine composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=directory::$(composer config cache-dir)"
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.directory }}
|
||||
key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ matrix.php }}-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo "::group::composer update"
|
||||
composer update --no-progress --no-suggest --ansi
|
||||
echo "::endgroup::"
|
||||
echo "::group::install phpunit"
|
||||
./phpunit install
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Run tests
|
||||
run: ./phpunit --verbose --group integration
|
||||
env:
|
||||
SYMFONY_DEPRECATIONS_HELPER: 'max[indirect]=7'
|
||||
REDIS_HOST: localhost
|
||||
REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005'
|
||||
MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages
|
||||
MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages
|
||||
MEMCACHED_HOST: localhost
|
||||
|
||||
- name: Run HTTP push tests
|
||||
if: matrix.php == '7.4'
|
||||
run: |
|
||||
[ -d .phpunit ] && mv .phpunit .phpunit.bak
|
||||
wget -q https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz -O - | tar xz && mv vulcain /usr/local/bin
|
||||
docker run --rm -e COMPOSER_ROOT_VERSION -e SYMFONY_VERSION -v $(pwd):/app -v $(which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.4-alpine ./phpunit --verbose src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push
|
||||
sudo rm -rf .phpunit
|
||||
[ -d .phpunit.bak ] && mv .phpunit.bak .phpunit
|
31
.travis.yml
31
.travis.yml
|
@ -13,14 +13,11 @@ addons:
|
|||
- slapd
|
||||
- zookeeperd
|
||||
- libzookeeper-mt-dev
|
||||
- rabbitmq-server
|
||||
|
||||
env:
|
||||
global:
|
||||
- MIN_PHP=7.2.5
|
||||
- SYMFONY_PROCESS_PHP_TEST_BINARY=~/.phpenv/shims/php
|
||||
- MESSENGER_AMQP_DSN=amqp://localhost/%2f/messages
|
||||
- MESSENGER_REDIS_DSN=redis://127.0.0.1:7006/messages
|
||||
- SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE=1
|
||||
|
||||
matrix:
|
||||
|
@ -39,13 +36,6 @@ cache:
|
|||
- php-$MIN_PHP
|
||||
- ~/php-ext
|
||||
|
||||
services:
|
||||
- memcached
|
||||
- mongodb
|
||||
- redis-server
|
||||
- rabbitmq
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- |
|
||||
# Enable Sury ppa
|
||||
|
@ -56,12 +46,6 @@ before_install:
|
|||
sudo apt update
|
||||
sudo apt install -y librabbitmq-dev libsodium-dev
|
||||
|
||||
- |
|
||||
# Start Redis cluster
|
||||
docker pull grokzen/redis-cluster:5.0.4
|
||||
docker run -d -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 -p 7006:7006 -p 7007:7007 -e "STANDALONE=true" --name redis-cluster grokzen/redis-cluster:5.0.4
|
||||
export REDIS_CLUSTER_HOSTS='localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005'
|
||||
|
||||
- |
|
||||
# General configuration
|
||||
set -e
|
||||
|
@ -141,12 +125,6 @@ before_install:
|
|||
(cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2)
|
||||
fi
|
||||
|
||||
- |
|
||||
# Install vulcain
|
||||
wget https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz -O - | tar xz
|
||||
sudo mv vulcain /usr/local/bin
|
||||
docker pull php:7.3-alpine
|
||||
|
||||
- |
|
||||
# php.ini configuration
|
||||
for PHP in $TRAVIS_PHP_VERSION $php_extra; do
|
||||
|
@ -268,15 +246,6 @@ install:
|
|||
export PHP=$1
|
||||
phpenv global $PHP
|
||||
|
||||
if [[ !$deps && $PHP = 7.2 ]]; then
|
||||
phpenv global $PHP
|
||||
tfold 'composer update' $COMPOSER_UP
|
||||
[ -d .phpunit ] && mv .phpunit .phpunit.bak
|
||||
tfold src/Symfony/Component/HttpClient.h2push "docker run -it --rm -v $(pwd):/app -v $(phpenv which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push"
|
||||
sudo rm .phpunit -rf
|
||||
[ -d .phpunit.bak ] && mv .phpunit.bak .phpunit
|
||||
fi
|
||||
|
||||
if [[ $PHP != 7.4* && $PHP != $TRAVIS_PHP_VERSION && $TRAVIS_PULL_REQUEST != false ]]; then
|
||||
echo -e "\\n\\e[33;1mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m"
|
||||
return
|
||||
|
|
|
@ -7,6 +7,56 @@ in 4.4 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 between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1
|
||||
|
||||
* 4.4.8 (2020-04-28)
|
||||
|
||||
* bug #36536 [Cache] Allow invalidateTags calls to be traced by data collector (l-vo)
|
||||
* bug #36566 [PhpUnitBridge] Use COMPOSER_BINARY env var if available (fancyweb)
|
||||
* bug #36560 [YAML] escape DEL(\x7f) (sdkawata)
|
||||
* bug #36539 [PhpUnitBridge] fix compatibility with phpunit 9 (garak)
|
||||
* bug #36555 [Cache] skip APCu in chains when the backend is disabled (nicolas-grekas)
|
||||
* bug #36523 [Form] apply automatically step=1 for datetime-local input (ottaviano)
|
||||
* bug #36519 [FrameworkBundle] debug:autowiring: Fix wrong display when using class_alias (weaverryan)
|
||||
* bug #36454 [DependencyInjection][ServiceSubscriber] Support late aliases (fancyweb)
|
||||
* bug #36498 [Security/Core] fix escape for username in LdapBindAuthenticationProvider.php (stoccc)
|
||||
* bug #36506 [FrameworkBundle] Fix session.attribute_bag service definition (fancyweb)
|
||||
* bug #36500 [Routing][PrefixTrait] Add the _locale requirement (fancyweb)
|
||||
* bug #36457 [Cache] CacheItem with tag is never a hit after expired (alexander-schranz, nicolas-grekas)
|
||||
* bug #36490 [HttpFoundation] workaround PHP bug in the session module (nicolas-grekas)
|
||||
* bug #36483 [SecurityBundle] fix accepting env vars in remember-me configurations (zek)
|
||||
* bug #36343 [Form] Fixed handling groups sequence validation (HeahDude)
|
||||
* bug #36460 [Cache] Avoid memory leak in TraceableAdapter::reset() (lyrixx)
|
||||
* bug #36467 Mailer from sender fixes (fabpot)
|
||||
* bug #36408 [PhpUnitBridge] add PolyfillTestCaseTrait::expectExceptionMessageMatches to provide FC with recent phpunit versions (soyuka)
|
||||
* bug #36447 Remove return type for Twig function workflow_metadata() (gisostallenberg)
|
||||
* bug #36449 [Messenger] Make sure redis transports are initialized correctly (Seldaek)
|
||||
* bug #36411 [Form] RepeatedType should always have inner types mapped (biozshock)
|
||||
* bug #36441 [DI] fix loading defaults when using the PHP-DSL (nicolas-grekas)
|
||||
* bug #36434 [HttpKernel] silence E_NOTICE triggered since PHP 7.4 (xabbuh)
|
||||
* bug #36365 [Validator] Fixed default group for nested composite constraints (HeahDude)
|
||||
* bug #36422 [HttpClient] fix HTTP/2 support on non-SSL connections - CurlHttpClient only (nicolas-grekas)
|
||||
* bug #36417 Force ping after transport exception (oesteve)
|
||||
* bug #35591 [Validator] do not merge constraints within interfaces (greedyivan)
|
||||
* bug #36377 [HttpClient] Fix scoped client without query option configuration (X-Coder264)
|
||||
* bug #36387 [DI] fix detecting short service syntax in yaml (nicolas-grekas)
|
||||
* bug #36392 [DI] add missing property declarations in InlineServiceConfigurator (nicolas-grekas)
|
||||
* bug #36400 Allowing empty secrets to be set (weaverryan)
|
||||
* bug #36380 [Process] Fixed input/output error on PHP 7.4 (mbardelmeijer)
|
||||
* bug #36376 [Workflow] Use a strict comparison when retrieving raw marking in MarkingStore (lyrixx)
|
||||
* bug #36375 [Workflow] Use a strict comparison when retrieving raw marking in MarkingStore (lyrixx)
|
||||
* bug #36305 [PropertyInfo][ReflectionExtractor] Check the array mutator prefixes last when the property is singular (fancyweb)
|
||||
* bug #35656 [HttpFoundation] Fixed session migration with custom cookie lifetime (Guite)
|
||||
* bug #36342 [HttpKernel][FrameworkBundle] fix compat with Debug component (nicolas-grekas)
|
||||
* bug #36315 [WebProfilerBundle] Support for Content Security Policy style-src-elem and script-src-elem in WebProfiler (ampaze)
|
||||
* bug #36286 [Validator] Allow URL-encoded special characters in basic auth part of URLs (cweiske)
|
||||
* bug #36335 [Security] Track session usage whenever a new token is set (wouterj)
|
||||
* bug #36332 [Serializer] Fix unitialized properties (from PHP 7.4.2) when serializing context for the cache key (alanpoulain)
|
||||
* bug #36337 [MonologBridge] Fix $level type (fancyweb)
|
||||
* bug #36223 [Security][Http][SwitchUserListener] Ignore all non existent username protection errors (fancyweb)
|
||||
* bug #36239 [HttpKernel][LoggerDataCollector] Prevent keys collisions in the sanitized logs processing (fancyweb)
|
||||
* bug #36245 [Validator] Fixed calling getters before resolving groups (HeahDude)
|
||||
* bug #36265 Fix the reporting of deprecations in twig:lint (stof)
|
||||
* bug #36283 [Security] forward multiple attributes voting flag (xabbuh)
|
||||
|
||||
* 4.4.7 (2020-03-30)
|
||||
|
||||
* security #cve-2020-5255 [HttpFoundation] Do not set the default Content-Type based on the Accept header (yceruto)
|
||||
|
|
|
@ -318,7 +318,14 @@ EOF
|
|||
{
|
||||
$filteredCatalogue = new MessageCatalogue($catalogue->getLocale());
|
||||
|
||||
if ($messages = $catalogue->all($domain)) {
|
||||
// extract intl-icu messages only
|
||||
$intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
if ($intlMessages = $catalogue->all($intlDomain)) {
|
||||
$filteredCatalogue->add($intlMessages, $intlDomain);
|
||||
}
|
||||
|
||||
// extract all messages and subtract intl-icu messages
|
||||
if ($messages = array_diff($catalogue->all($domain), $intlMessages)) {
|
||||
$filteredCatalogue->add($messages, $domain);
|
||||
}
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
|
|
|
@ -26,9 +26,12 @@ class CachePoolsTest extends AbstractWebTestCase
|
|||
|
||||
/**
|
||||
* @requires extension redis
|
||||
* @group integration
|
||||
*/
|
||||
public function testRedisCachePools()
|
||||
{
|
||||
$this->skipIfRedisUnavailable();
|
||||
|
||||
try {
|
||||
$this->doTestCachePools(['root_config' => 'redis_config.yml', 'environment' => 'redis_cache'], RedisAdapter::class);
|
||||
} catch (\PHPUnit\Framework\Error\Warning $e) {
|
||||
|
@ -51,9 +54,12 @@ class CachePoolsTest extends AbstractWebTestCase
|
|||
|
||||
/**
|
||||
* @requires extension redis
|
||||
* @group integration
|
||||
*/
|
||||
public function testRedisCustomCachePools()
|
||||
{
|
||||
$this->skipIfRedisUnavailable();
|
||||
|
||||
try {
|
||||
$this->doTestCachePools(['root_config' => 'redis_custom_config.yml', 'environment' => 'custom_redis_cache'], RedisAdapter::class);
|
||||
} catch (\PHPUnit\Framework\Error\Warning $e) {
|
||||
|
@ -121,4 +127,13 @@ class CachePoolsTest extends AbstractWebTestCase
|
|||
{
|
||||
return parent::createKernel(['test_case' => 'CachePools'] + $options);
|
||||
}
|
||||
|
||||
private function skipIfRedisUnavailable()
|
||||
{
|
||||
try {
|
||||
(new \Redis())->connect(getenv('REDIS_HOST'));
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,12 +129,11 @@ class ContentSecurityPolicyHandler
|
|||
continue;
|
||||
}
|
||||
if (!isset($headers[$header][$type])) {
|
||||
if (isset($headers[$header]['default-src'])) {
|
||||
$headers[$header][$type] = $headers[$header]['default-src'];
|
||||
} else {
|
||||
// If there is no script-src/style-src and no default-src, no additional rules required.
|
||||
if (null === $fallback = $this->getDirectiveFallback($directives, $type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$headers[$header][$type] = $fallback;
|
||||
}
|
||||
$ruleIsSet = true;
|
||||
if (!\in_array('\'unsafe-inline\'', $headers[$header][$type], true)) {
|
||||
|
@ -199,9 +198,7 @@ class ContentSecurityPolicyHandler
|
|||
{
|
||||
if (isset($directivesSet[$type])) {
|
||||
$directives = $directivesSet[$type];
|
||||
} elseif (isset($directivesSet['default-src'])) {
|
||||
$directives = $directivesSet['default-src'];
|
||||
} else {
|
||||
} elseif (null === $directives = $this->getDirectiveFallback($directivesSet, $type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -225,6 +222,16 @@ class ContentSecurityPolicyHandler
|
|||
return false;
|
||||
}
|
||||
|
||||
private function getDirectiveFallback(array $directiveSet, $type)
|
||||
{
|
||||
if (\in_array($type, ['script-src-elem', 'style-src-elem'], true) || !isset($directiveSet['default-src'])) {
|
||||
// Let the browser fallback on it's own
|
||||
return null;
|
||||
}
|
||||
|
||||
return $directiveSet['default-src'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Content-Security-Policy headers (either X-Content-Security-Policy or Content-Security-Policy) from
|
||||
* a response.
|
||||
|
|
|
@ -131,7 +131,14 @@ class ContentSecurityPolicyHandlerTest extends TestCase
|
|||
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
|
||||
$this->createRequest(),
|
||||
$this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']),
|
||||
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
|
||||
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
|
||||
],
|
||||
[
|
||||
$nonce,
|
||||
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
|
||||
$this->createRequest(),
|
||||
$this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'']),
|
||||
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
|
||||
],
|
||||
[
|
||||
$nonce,
|
||||
|
|
|
@ -14,8 +14,8 @@ namespace Symfony\Component\Cache\Adapter;
|
|||
use Predis\Connection\Aggregate\ClusterInterface;
|
||||
use Predis\Connection\Aggregate\PredisCluster;
|
||||
use Predis\Response\Status;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Exception\LogicException;
|
||||
use Symfony\Component\Cache\Marshaller\DeflateMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||
|
@ -95,9 +95,7 @@ class RedisTagAwareAdapter extends AbstractTagAwareAdapter
|
|||
{
|
||||
$eviction = $this->getRedisEvictionPolicy();
|
||||
if ('noeviction' !== $eviction && 0 !== strpos($eviction, 'volatile-')) {
|
||||
CacheItem::log($this->logger, sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies', $eviction));
|
||||
|
||||
return false;
|
||||
throw new LogicException(sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies.', $eviction));
|
||||
}
|
||||
|
||||
// serialize values
|
||||
|
|
|
@ -34,9 +34,10 @@ abstract class AbstractRedisAdapterTest extends AdapterTestCase
|
|||
if (!\extension_loaded('redis')) {
|
||||
self::markTestSkipped('Extension redis required.');
|
||||
}
|
||||
if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
|
||||
$e = error_get_last();
|
||||
self::markTestSkipped($e['message']);
|
||||
try {
|
||||
(new \Redis())->connect(getenv('REDIS_HOST'));
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ use Psr\Cache\CacheItemPoolInterface;
|
|||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class MemcachedAdapterTest extends AdapterTestCase
|
||||
{
|
||||
protected $skippedTests = [
|
||||
|
|
|
@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Tests\Adapter;
|
|||
use Predis\Connection\StreamConnection;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisClusterAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -13,6 +13,9 @@ namespace Symfony\Component\Cache\Tests\Adapter;
|
|||
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisRedisClusterAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -15,6 +15,9 @@ use Psr\Cache\CacheItemPoolInterface;
|
|||
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
|
||||
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisTagAwareAdapterTest extends PredisAdapterTest
|
||||
{
|
||||
use TagAwareTestTrait;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Psr\Cache\CacheItemPoolInterface;
|
|||
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
|
||||
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisTagAwareClusterAdapterTest extends PredisClusterAdapterTest
|
||||
{
|
||||
use TagAwareTestTrait;
|
||||
|
|
|
@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Tests\Adapter;
|
|||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisAdapterSentinelTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -16,6 +16,9 @@ use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
|||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisArrayAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -16,6 +16,9 @@ use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
|||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisClusterAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -16,6 +16,9 @@ use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
|
|||
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisTagAwareAdapterTest extends RedisAdapterTest
|
||||
{
|
||||
use TagAwareTestTrait;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Psr\Cache\CacheItemPoolInterface;
|
|||
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
|
||||
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisTagAwareArrayAdapterTest extends RedisArrayAdapterTest
|
||||
{
|
||||
use TagAwareTestTrait;
|
||||
|
|
|
@ -16,6 +16,9 @@ use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
|
|||
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisTagAwareClusterAdapterTest extends RedisClusterAdapterTest
|
||||
{
|
||||
use TagAwareTestTrait;
|
||||
|
|
|
@ -435,7 +435,7 @@ class QuestionHelper extends Helper
|
|||
|
||||
if (false !== $shell = $this->getShell()) {
|
||||
$readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
|
||||
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
|
||||
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword' 2> /dev/null", $shell, $readCmd);
|
||||
$sCommand = shell_exec($command);
|
||||
$value = $trimmable ? rtrim($sCommand) : $sCommand;
|
||||
$output->writeln('');
|
||||
|
@ -459,6 +459,11 @@ class QuestionHelper extends Helper
|
|||
{
|
||||
$error = null;
|
||||
$attempts = $question->getMaxAttempts();
|
||||
|
||||
if (null === $attempts && !$this->isTty()) {
|
||||
$attempts = 1;
|
||||
}
|
||||
|
||||
while (null === $attempts || $attempts--) {
|
||||
if (null !== $error) {
|
||||
$this->writeError($output, $error);
|
||||
|
@ -501,4 +506,19 @@ class QuestionHelper extends Helper
|
|||
|
||||
return self::$shell;
|
||||
}
|
||||
|
||||
private function isTty(): bool
|
||||
{
|
||||
$inputStream = !$this->inputStream && \defined('STDIN') ? STDIN : $this->inputStream;
|
||||
|
||||
if (\function_exists('stream_isatty')) {
|
||||
return stream_isatty($inputStream);
|
||||
}
|
||||
|
||||
if (!\function_exists('posix_isatty')) {
|
||||
return posix_isatty($inputStream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -726,6 +726,23 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||
$dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question);
|
||||
}
|
||||
|
||||
public function testAskThrowsExceptionFromValidatorEarlyWhenTtyIsMissing()
|
||||
{
|
||||
$this->expectException('Exception');
|
||||
$this->expectExceptionMessage('Bar, not Foo');
|
||||
|
||||
$output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
|
||||
$output->expects($this->once())->method('writeln');
|
||||
|
||||
(new QuestionHelper())->ask(
|
||||
$this->createStreamableInputInterfaceMock($this->getInputStream('Foo'), true),
|
||||
$output,
|
||||
(new Question('Q?'))->setHidden(true)->setValidator(function ($input) {
|
||||
throw new \Exception("Bar, not $input");
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public function testEmptyChoices()
|
||||
{
|
||||
$this->expectException('LogicException');
|
||||
|
|
|
@ -682,7 +682,7 @@ class YamlFileLoader extends FileLoader
|
|||
try {
|
||||
$configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
|
||||
} catch (ParseException $e) {
|
||||
throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: '.$e->getMessage(), $file), 0, $e);
|
||||
throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML', $file).': '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
return $this->validate($configuration, $file);
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
$exceptionAsArray = $exception->toArray();
|
||||
$exceptionWithUserCode = [];
|
||||
$exceptionAsArrayCount = count($exceptionAsArray);
|
||||
$last = count($exceptionAsArray) - 1;
|
||||
$last = $exceptionAsArrayCount - 1;
|
||||
foreach ($exceptionAsArray as $i => $e) {
|
||||
foreach ($e['trace'] as $trace) {
|
||||
if ($trace['file'] && false === mb_strpos($trace['file'], '/vendor/') && false === mb_strpos($trace['file'], '/var/cache/') && $i < $last) {
|
||||
|
|
|
@ -14,10 +14,7 @@
|
|||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony ErrorHandler Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Symfony ErrorHandler Extension Test Suite">
|
||||
<directory suffix=".phpt">./Resources/ext/tests/</directory>
|
||||
<directory suffix=".phpt">./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
|
|
|
@ -434,28 +434,19 @@ class Filesystem
|
|||
$startPath = str_replace('\\', '/', $startPath);
|
||||
}
|
||||
|
||||
$stripDriveLetter = function ($path) {
|
||||
if (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) {
|
||||
return substr($path, 2);
|
||||
}
|
||||
|
||||
return $path;
|
||||
$splitDriveLetter = function ($path) {
|
||||
return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0]))
|
||||
? [substr($path, 2), strtoupper($path[0])]
|
||||
: [$path, null];
|
||||
};
|
||||
|
||||
$endPath = $stripDriveLetter($endPath);
|
||||
$startPath = $stripDriveLetter($startPath);
|
||||
|
||||
// Split the paths into arrays
|
||||
$startPathArr = explode('/', trim($startPath, '/'));
|
||||
$endPathArr = explode('/', trim($endPath, '/'));
|
||||
|
||||
$normalizePathArray = function ($pathSegments) {
|
||||
$splitPath = function ($path) {
|
||||
$result = [];
|
||||
|
||||
foreach ($pathSegments as $segment) {
|
||||
foreach (explode('/', trim($path, '/')) as $segment) {
|
||||
if ('..' === $segment) {
|
||||
array_pop($result);
|
||||
} elseif ('.' !== $segment) {
|
||||
} elseif ('.' !== $segment && '' !== $segment) {
|
||||
$result[] = $segment;
|
||||
}
|
||||
}
|
||||
|
@ -463,8 +454,16 @@ class Filesystem
|
|||
return $result;
|
||||
};
|
||||
|
||||
$startPathArr = $normalizePathArray($startPathArr);
|
||||
$endPathArr = $normalizePathArray($endPathArr);
|
||||
list($endPath, $endDriveLetter) = $splitDriveLetter($endPath);
|
||||
list($startPath, $startDriveLetter) = $splitDriveLetter($startPath);
|
||||
|
||||
$startPathArr = $splitPath($startPath);
|
||||
$endPathArr = $splitPath($endPath);
|
||||
|
||||
if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) {
|
||||
// End path is on another drive, so no relative path exists
|
||||
return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : '');
|
||||
}
|
||||
|
||||
// Find for which directory the common path stops
|
||||
$index = 0;
|
||||
|
|
|
@ -1151,10 +1151,14 @@ class FilesystemTest extends FilesystemTestCase
|
|||
['/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'],
|
||||
['/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'],
|
||||
['C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'],
|
||||
['C:/aa/bb/cc', 'c:/aa/dd/..', 'bb/cc/'],
|
||||
['c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'],
|
||||
['C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'],
|
||||
['C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'],
|
||||
['C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'],
|
||||
['D:/', 'C:/aa/../bb/cc', 'D:/'],
|
||||
['D:/aa/bb', 'C:/aa', 'D:/aa/bb/'],
|
||||
['D:/../../aa/../bb/cc', 'C:/aa/dd/..', 'D:/bb/cc/'],
|
||||
];
|
||||
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
|
|
|
@ -62,7 +62,7 @@ class DependencyInjectionExtension implements FormExtensionInterface
|
|||
$extensions = [];
|
||||
|
||||
if (isset($this->typeExtensionServices[$name])) {
|
||||
foreach ($this->typeExtensionServices[$name] as $serviceId => $extension) {
|
||||
foreach ($this->typeExtensionServices[$name] as $extension) {
|
||||
$extensions[] = $extension;
|
||||
|
||||
$extendedTypes = [];
|
||||
|
@ -72,7 +72,7 @@ class DependencyInjectionExtension implements FormExtensionInterface
|
|||
|
||||
// validate the result of getExtendedTypes() to ensure it is consistent with the service definition
|
||||
if (!\in_array($name, $extendedTypes, true)) {
|
||||
throw new InvalidArgumentException(sprintf('The extended type specified for the service "%s" does not match the actual extended type. Expected "%s", given "%s".', $serviceId, $name, implode(', ', $extendedTypes)));
|
||||
throw new InvalidArgumentException(sprintf('The extended type "%s" specified for the type extension class "%s" does not match any of the actual extended types (["%s"]).', $name, \get_class($extension), implode('", "', $extendedTypes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -958,7 +958,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac
|
|||
*
|
||||
* @return FormInterface The child form
|
||||
*
|
||||
* @throws \OutOfBoundsException if the named child does not exist
|
||||
* @throws OutOfBoundsException if the named child does not exist
|
||||
*/
|
||||
public function offsetGet($name)
|
||||
{
|
||||
|
|
|
@ -60,7 +60,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws \OutOfBoundsException if the named child does not exist
|
||||
* @throws Exception\OutOfBoundsException if the named child does not exist
|
||||
*/
|
||||
public function get(string $name);
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ class DependencyInjectionExtensionTest extends TestCase
|
|||
public function testThrowExceptionForInvalidExtendedType()
|
||||
{
|
||||
$this->expectException('Symfony\Component\Form\Exception\InvalidArgumentException');
|
||||
$this->expectExceptionMessage(sprintf('The extended type "unmatched" specified for the type extension class "%s" does not match any of the actual extended types (["test"]).', TestTypeExtension::class));
|
||||
|
||||
$extensions = [
|
||||
'unmatched' => new \ArrayIterator([new TestTypeExtension()]),
|
||||
];
|
||||
|
|
|
@ -44,6 +44,11 @@ abstract class AbstractRedisSessionHandlerTestCase extends TestCase
|
|||
if (!\extension_loaded('redis')) {
|
||||
self::markTestSkipped('Extension redis required.');
|
||||
}
|
||||
try {
|
||||
(new \Redis())->connect(getenv('REDIS_HOST'));
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
|
||||
$host = getenv('REDIS_HOST') ?: 'localhost';
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
|||
|
||||
use Predis\Client;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisClusterSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,9 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
|||
|
||||
use Predis\Client;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class PredisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisArraySessionHandlerTest extends AbstractRedisSessionHandlerTestCase
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisClusterSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class RedisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -294,30 +294,30 @@ class InflectorTest extends TestCase
|
|||
/**
|
||||
* @dataProvider singularizeProvider
|
||||
*/
|
||||
public function testSingularize($plural, $singular)
|
||||
public function testSingularize($plural, $expectedSingular)
|
||||
{
|
||||
$single = Inflector::singularize($plural);
|
||||
if (\is_string($singular) && \is_array($single)) {
|
||||
$this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single));
|
||||
} elseif (\is_array($singular) && \is_string($single)) {
|
||||
$this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single);
|
||||
$singular = Inflector::singularize($plural);
|
||||
if (\is_string($expectedSingular) && \is_array($singular)) {
|
||||
$this->fail("--- Expected\n`string`: ".$expectedSingular."\n+++ Actual\n`array`: ".implode(', ', $singular));
|
||||
} elseif (\is_array($expectedSingular) && \is_string($singular)) {
|
||||
$this->fail("--- Expected\n`array`: ".implode(', ', $expectedSingular)."\n+++ Actual\n`string`: ".$singular);
|
||||
}
|
||||
|
||||
$this->assertEquals($singular, $single);
|
||||
$this->assertEquals($expectedSingular, $singular);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider pluralizeProvider
|
||||
*/
|
||||
public function testPluralize($plural, $singular)
|
||||
public function testPluralize($singular, $expectedPlural)
|
||||
{
|
||||
$single = Inflector::pluralize($plural);
|
||||
if (\is_string($singular) && \is_array($single)) {
|
||||
$this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single));
|
||||
} elseif (\is_array($singular) && \is_string($single)) {
|
||||
$this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single);
|
||||
$plural = Inflector::pluralize($singular);
|
||||
if (\is_string($expectedPlural) && \is_array($plural)) {
|
||||
$this->fail("--- Expected\n`string`: ".$expectedPlural."\n+++ Actual\n`array`: ".implode(', ', $plural));
|
||||
} elseif (\is_array($expectedPlural) && \is_string($plural)) {
|
||||
$this->fail("--- Expected\n`array`: ".implode(', ', $expectedPlural)."\n+++ Actual\n`string`: ".$plural);
|
||||
}
|
||||
|
||||
$this->assertEquals($singular, $single);
|
||||
$this->assertEquals($expectedPlural, $plural);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use Symfony\Component\Lock\Store\MemcachedStore;
|
|||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension memcached
|
||||
* @group integration
|
||||
*/
|
||||
class MemcachedStoreTest extends AbstractStoreTest
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Lock\Tests\Store;
|
|||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
* @group integration
|
||||
*/
|
||||
class PredisStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Symfony\Component\Lock\Tests\Store;
|
|||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension redis
|
||||
* @group integration
|
||||
*/
|
||||
class RedisArrayStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
|
@ -23,9 +24,10 @@ class RedisArrayStoreTest extends AbstractRedisStoreTest
|
|||
if (!class_exists('RedisArray')) {
|
||||
self::markTestSkipped('The RedisArray class is required.');
|
||||
}
|
||||
if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
|
||||
$e = error_get_last();
|
||||
self::markTestSkipped($e['message']);
|
||||
try {
|
||||
(new \Redis())->connect(getenv('REDIS_HOST'));
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Symfony\Component\Lock\Tests\Store;
|
|||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension redis
|
||||
* @group integration
|
||||
*/
|
||||
class RedisClusterStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
|
|
|
@ -17,14 +17,16 @@ use Symfony\Component\Lock\Store\RedisStore;
|
|||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension redis
|
||||
* @group integration
|
||||
*/
|
||||
class RedisStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
|
||||
$e = error_get_last();
|
||||
self::markTestSkipped($e['message']);
|
||||
try {
|
||||
(new \Redis())->connect(getenv('REDIS_HOST'));
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
|||
|
||||
/**
|
||||
* @requires extension amqp
|
||||
* @group integration
|
||||
*/
|
||||
class AmqpExtIntegrationTest extends TestCase
|
||||
{
|
||||
|
|
|
@ -17,14 +17,14 @@ use Symfony\Component\Messenger\Transport\RedisExt\Connection;
|
|||
|
||||
/**
|
||||
* @requires extension redis >= 4.3.0
|
||||
* @group integration
|
||||
*/
|
||||
class ConnectionTest extends TestCase
|
||||
{
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
$redis = Connection::fromDsn('redis://localhost/queue');
|
||||
|
||||
try {
|
||||
$redis = Connection::fromDsn('redis://localhost/queue');
|
||||
$redis->get();
|
||||
} catch (TransportException $e) {
|
||||
if (0 === strpos($e->getMessage(), 'ERR unknown command \'X')) {
|
||||
|
@ -32,6 +32,8 @@ class ConnectionTest extends TestCase
|
|||
}
|
||||
|
||||
throw $e;
|
||||
} catch (\RedisException $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ use Symfony\Component\Messenger\Transport\RedisExt\Connection;
|
|||
/**
|
||||
* @requires extension redis
|
||||
* @group time-sensitive
|
||||
* @group integration
|
||||
*/
|
||||
class RedisExtIntegrationTest extends TestCase
|
||||
{
|
||||
|
@ -30,10 +31,14 @@ class RedisExtIntegrationTest extends TestCase
|
|||
$this->markTestSkipped('The "MESSENGER_REDIS_DSN" environment variable is required.');
|
||||
}
|
||||
|
||||
$this->redis = new \Redis();
|
||||
$this->connection = Connection::fromDsn(getenv('MESSENGER_REDIS_DSN'), [], $this->redis);
|
||||
$this->connection->cleanup();
|
||||
$this->connection->setup();
|
||||
try {
|
||||
$this->redis = new \Redis();
|
||||
$this->connection = Connection::fromDsn(getenv('MESSENGER_REDIS_DSN'), [], $this->redis);
|
||||
$this->connection->cleanup();
|
||||
$this->connection->setup();
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testConnectionSendAndGet()
|
||||
|
|
|
@ -31,12 +31,26 @@ class RedisTransportFactoryTest extends TestCase
|
|||
$this->assertFalse($factory->supports('invalid-dsn', []));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
public function testCreateTransport()
|
||||
{
|
||||
$this->skipIfRedisUnavailable();
|
||||
|
||||
$factory = new RedisTransportFactory();
|
||||
$serializer = $this->getMockBuilder(SerializerInterface::class)->getMock();
|
||||
$expectedTransport = new RedisTransport(Connection::fromDsn('redis://localhost', ['foo' => 'bar']), $serializer);
|
||||
$expectedTransport = new RedisTransport(Connection::fromDsn('redis://'.getenv('REDIS_HOST'), ['foo' => 'bar']), $serializer);
|
||||
|
||||
$this->assertEquals($expectedTransport, $factory->createTransport('redis://localhost', ['foo' => 'bar'], $serializer));
|
||||
$this->assertEquals($expectedTransport, $factory->createTransport('redis://'.getenv('REDIS_HOST'), ['foo' => 'bar'], $serializer));
|
||||
}
|
||||
|
||||
private function skipIfRedisUnavailable()
|
||||
{
|
||||
try {
|
||||
(new \Redis())->connect(getenv('REDIS_HOST'));
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ class YamlFileLoader extends FileLoader
|
|||
try {
|
||||
$parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
|
||||
} catch (ParseException $e) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML', $path).': '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$collection = new RouteCollection();
|
||||
|
|
|
@ -151,6 +151,10 @@ class Serializer implements SerializerInterface, ContextAwareNormalizerInterface
|
|||
}
|
||||
|
||||
if (\is_array($data) || $data instanceof \Traversable) {
|
||||
if ($data instanceof \Countable && 0 === $data->count()) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$normalized = [];
|
||||
foreach ($data as $key => $val) {
|
||||
$normalized[$key] = $this->normalize($val, $format, $context);
|
||||
|
|
|
@ -25,6 +25,7 @@ use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
|
|||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
|
@ -488,6 +489,26 @@ class SerializerTest extends TestCase
|
|||
(new Serializer())->normalize(tmpfile());
|
||||
}
|
||||
|
||||
public function testNormalizePreserveEmptyArrayObject()
|
||||
{
|
||||
$serializer = new Serializer(
|
||||
[
|
||||
new PropertyNormalizer(),
|
||||
new ObjectNormalizer(),
|
||||
new ArrayDenormalizer(),
|
||||
],
|
||||
[
|
||||
'json' => new JsonEncoder(),
|
||||
]
|
||||
);
|
||||
|
||||
$object = [];
|
||||
$object['foo'] = new \ArrayObject();
|
||||
$object['bar'] = new \ArrayObject(['notempty']);
|
||||
$object['baz'] = new \ArrayObject(['nested' => new \ArrayObject()]);
|
||||
$this->assertEquals('{"foo":{},"bar":["notempty"],"baz":{"nested":{}}}', $serializer->serialize($object, 'json', [AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true]));
|
||||
}
|
||||
|
||||
private function serializerWithClassDiscriminator()
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
|
|
|
@ -42,7 +42,7 @@ class YamlFileLoader extends FileLoader
|
|||
try {
|
||||
$messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT);
|
||||
} catch (ParseException $e) {
|
||||
throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s".', $resource), 0, $e);
|
||||
throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML', $resource).': '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
if (null !== $messages && !\is_array($messages)) {
|
||||
|
|
|
@ -68,6 +68,11 @@ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterf
|
|||
public function all(string $domain = null)
|
||||
{
|
||||
if (null !== $domain) {
|
||||
// skip messages merge if intl-icu requested explicitly
|
||||
if (false !== strpos($domain, self::INTL_DOMAIN_SUFFIX)) {
|
||||
return $this->messages[$domain] ?? [];
|
||||
}
|
||||
|
||||
return ($this->messages[$domain.self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,30 @@ class MessageCatalogueTest extends TestCase
|
|||
$this->assertEquals($messages, $catalogue->all());
|
||||
}
|
||||
|
||||
public function testAllIntICU()
|
||||
{
|
||||
$messages = [
|
||||
'domain1+intl-icu' => ['foo' => 'bar'],
|
||||
'domain2+intl-icu' => ['bar' => 'foo'],
|
||||
'domain2' => ['biz' => 'biz'],
|
||||
];
|
||||
$catalogue = new MessageCatalogue('en', $messages);
|
||||
|
||||
// separated domains
|
||||
$this->assertSame(['foo' => 'bar'], $catalogue->all('domain1+intl-icu'));
|
||||
$this->assertSame(['bar' => 'foo'], $catalogue->all('domain2+intl-icu'));
|
||||
|
||||
// merged, intl-icu ignored
|
||||
$this->assertSame(['bar' => 'foo', 'biz' => 'biz'], $catalogue->all('domain2'));
|
||||
|
||||
// intl-icu ignored
|
||||
$messagesExpected = [
|
||||
'domain1' => ['foo' => 'bar'],
|
||||
'domain2' => ['bar' => 'foo', 'biz' => 'biz'],
|
||||
];
|
||||
$this->assertSame($messagesExpected, $catalogue->all());
|
||||
}
|
||||
|
||||
public function testHas()
|
||||
{
|
||||
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo'], 'domain2+intl-icu' => ['bar' => 'bar']]);
|
||||
|
|
|
@ -60,6 +60,9 @@ class EmailValidator extends ConstraintValidator
|
|||
}
|
||||
|
||||
$value = (string) $value;
|
||||
if ('' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $constraint->normalizer) {
|
||||
$value = ($constraint->normalizer)($value);
|
||||
|
|
|
@ -114,7 +114,7 @@ class YamlFileLoader extends FileLoader
|
|||
try {
|
||||
$classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
|
||||
} catch (ParseException $e) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML', $path).': '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
// empty file
|
||||
|
|
|
@ -352,7 +352,7 @@
|
|||
</trans-unit>
|
||||
<trans-unit id="91">
|
||||
<source>This value should be either negative or zero.</source>
|
||||
<target>Ova vrednost bi trebala biti pozitivna ili nula.</target>
|
||||
<target>Ova vrednost bi trebala biti negativna ili nula.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="92">
|
||||
<source>This value is not a valid timezone.</source>
|
||||
|
|
|
@ -46,6 +46,13 @@ class EmailValidatorTest extends ConstraintValidatorTestCase
|
|||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public function testObjectEmptyStringIsValid()
|
||||
{
|
||||
$this->validator->validate(new EmptyEmailObject(), new Email());
|
||||
|
||||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public function testExpectsStringCompatibleType()
|
||||
{
|
||||
$this->expectException('Symfony\Component\Validator\Exception\UnexpectedValueException');
|
||||
|
@ -300,3 +307,11 @@ class EmailValidatorTest extends ConstraintValidatorTestCase
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyEmailObject
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,11 @@ class Entity extends EntityParent implements EntityInterfaceB
|
|||
$this->internal = $internal;
|
||||
}
|
||||
|
||||
public function getFirstName()
|
||||
{
|
||||
return $this->firstName;
|
||||
}
|
||||
|
||||
public function getInternal()
|
||||
{
|
||||
return $this->internal.' from getter';
|
||||
|
@ -141,4 +146,9 @@ class Entity extends EntityParent implements EntityInterfaceB
|
|||
{
|
||||
$this->childB = $childB;
|
||||
}
|
||||
|
||||
public function getReference()
|
||||
{
|
||||
return $this->reference;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
|
||||
const REFERENCE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Reference';
|
||||
|
||||
const LAZY_PROPERTY = 'Symfony\Component\Validator\Validator\LazyProperty';
|
||||
|
||||
/**
|
||||
* @var FakeMetadataFactory
|
||||
*/
|
||||
|
@ -54,6 +56,7 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->referenceMetadata = new ClassMetadata(self::REFERENCE_CLASS);
|
||||
$this->metadataFactory->addMetadata($this->metadata);
|
||||
$this->metadataFactory->addMetadata($this->referenceMetadata);
|
||||
$this->metadataFactory->addMetadata(new ClassMetadata(self::LAZY_PROPERTY));
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
|
@ -510,7 +513,10 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->validate($entity);
|
||||
}
|
||||
|
||||
public function testArrayReference()
|
||||
/**
|
||||
* @dataProvider getConstraintMethods
|
||||
*/
|
||||
public function testArrayReference($constraintMethod)
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = ['key' => new Reference()];
|
||||
|
@ -528,7 +534,7 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$context->addViolation('Message %param%', ['%param%' => 'value']);
|
||||
};
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid());
|
||||
$this->metadata->$constraintMethod('reference', new Valid());
|
||||
$this->referenceMetadata->addConstraint(new Callback([
|
||||
'callback' => $callback,
|
||||
'groups' => 'Group',
|
||||
|
@ -548,8 +554,10 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->assertNull($violations[0]->getCode());
|
||||
}
|
||||
|
||||
// https://github.com/symfony/symfony/issues/6246
|
||||
public function testRecursiveArrayReference()
|
||||
/**
|
||||
* @dataProvider getConstraintMethods
|
||||
*/
|
||||
public function testRecursiveArrayReference($constraintMethod)
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = [2 => ['key' => new Reference()]];
|
||||
|
@ -567,7 +575,7 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$context->addViolation('Message %param%', ['%param%' => 'value']);
|
||||
};
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid());
|
||||
$this->metadata->$constraintMethod('reference', new Valid());
|
||||
$this->referenceMetadata->addConstraint(new Callback([
|
||||
'callback' => $callback,
|
||||
'groups' => 'Group',
|
||||
|
@ -611,7 +619,10 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->assertCount(0, $violations);
|
||||
}
|
||||
|
||||
public function testArrayTraversalCannotBeDisabled()
|
||||
/**
|
||||
* @dataProvider getConstraintMethods
|
||||
*/
|
||||
public function testArrayTraversalCannotBeDisabled($constraintMethod)
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = ['key' => new Reference()];
|
||||
|
@ -620,7 +631,7 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$context->addViolation('Message %param%', ['%param%' => 'value']);
|
||||
};
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid([
|
||||
$this->metadata->$constraintMethod('reference', new Valid([
|
||||
'traverse' => false,
|
||||
]));
|
||||
$this->referenceMetadata->addConstraint(new Callback($callback));
|
||||
|
@ -631,7 +642,10 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->assertCount(1, $violations);
|
||||
}
|
||||
|
||||
public function testRecursiveArrayTraversalCannotBeDisabled()
|
||||
/**
|
||||
* @dataProvider getConstraintMethods
|
||||
*/
|
||||
public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod)
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = [2 => ['key' => new Reference()]];
|
||||
|
@ -640,9 +654,10 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$context->addViolation('Message %param%', ['%param%' => 'value']);
|
||||
};
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid([
|
||||
$this->metadata->$constraintMethod('reference', new Valid([
|
||||
'traverse' => false,
|
||||
]));
|
||||
|
||||
$this->referenceMetadata->addConstraint(new Callback($callback));
|
||||
|
||||
$violations = $this->validate($entity);
|
||||
|
@ -651,12 +666,15 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->assertCount(1, $violations);
|
||||
}
|
||||
|
||||
public function testIgnoreScalarsDuringArrayTraversal()
|
||||
/**
|
||||
* @dataProvider getConstraintMethods
|
||||
*/
|
||||
public function testIgnoreScalarsDuringArrayTraversal($constraintMethod)
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = ['string', 1234];
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid());
|
||||
$this->metadata->$constraintMethod('reference', new Valid());
|
||||
|
||||
$violations = $this->validate($entity);
|
||||
|
||||
|
@ -664,12 +682,15 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
$this->assertCount(0, $violations);
|
||||
}
|
||||
|
||||
public function testIgnoreNullDuringArrayTraversal()
|
||||
/**
|
||||
* @dataProvider getConstraintMethods
|
||||
*/
|
||||
public function testIgnoreNullDuringArrayTraversal($constraintMethod)
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = [null];
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid());
|
||||
$this->metadata->$constraintMethod('reference', new Valid());
|
||||
|
||||
$violations = $this->validate($entity);
|
||||
|
||||
|
@ -1218,6 +1239,14 @@ abstract class AbstractValidatorTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public function getConstraintMethods()
|
||||
{
|
||||
return [
|
||||
['addPropertyConstraint'],
|
||||
['addGetterConstraint'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTestReplaceDefaultGroup()
|
||||
{
|
||||
return [
|
||||
|
|
|
@ -627,6 +627,10 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||
// See validateClassNode()
|
||||
$cascadedGroups = null !== $cascadedGroups && \count($cascadedGroups) > 0 ? $cascadedGroups : $groups;
|
||||
|
||||
if ($value instanceof LazyProperty) {
|
||||
$value = $value->getPropertyValue();
|
||||
}
|
||||
|
||||
if (\is_array($value)) {
|
||||
// Arrays are always traversed, independent of the specified
|
||||
// traversal strategy
|
||||
|
|
|
@ -17,6 +17,7 @@ use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
|
|||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @requires extension redis
|
||||
* @group integration
|
||||
*/
|
||||
class RedisCasterTest extends TestCase
|
||||
{
|
||||
|
@ -37,16 +38,18 @@ EODUMP;
|
|||
|
||||
public function testConnected()
|
||||
{
|
||||
$redisHost = getenv('REDIS_HOST');
|
||||
$redis = new \Redis();
|
||||
if (!@$redis->connect('127.0.0.1')) {
|
||||
$e = error_get_last();
|
||||
self::markTestSkipped($e['message']);
|
||||
try {
|
||||
$redis->connect($redisHost);
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
|
||||
$xCast = <<<'EODUMP'
|
||||
$xCast = <<<EODUMP
|
||||
Redis {%A
|
||||
isConnected: true
|
||||
host: "127.0.0.1"
|
||||
host: "$redisHost"
|
||||
port: 6379
|
||||
auth: null
|
||||
mode: ATOMIC
|
||||
|
|
|
@ -618,8 +618,14 @@ class Parser
|
|||
}
|
||||
|
||||
$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
|
||||
$isItComment = $this->isCurrentLineComment();
|
||||
|
||||
while ($this->moveToNextLine()) {
|
||||
if ($isItComment && !$isItUnindentedCollection) {
|
||||
$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
|
||||
$isItComment = $this->isCurrentLineComment();
|
||||
}
|
||||
|
||||
$indent = $this->getCurrentLineIndentation();
|
||||
|
||||
if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
|
||||
|
|
|
@ -74,3 +74,17 @@ yaml: |
|
|||
'foo #': baz
|
||||
php: |
|
||||
['foo #' => 'baz']
|
||||
---
|
||||
test: Comment before first item in unindented collection
|
||||
brief: >
|
||||
Comment directly before unindented collection is allowed
|
||||
yaml: |
|
||||
collection1:
|
||||
# comment
|
||||
- a
|
||||
- b
|
||||
collection2:
|
||||
- a
|
||||
- b
|
||||
php: |
|
||||
['collection1' => ['a', 'b'], 'collection2' => ['a', 'b']]
|
||||
|
|
Reference in New Issue