diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php index 75798d2799..60ca07b840 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -95,10 +95,7 @@ class IdReader } if (!$this->om->contains($object)) { - throw new RuntimeException( - 'Entities passed to the choice field must be managed. Maybe '. - 'persist them in the entity manager?' - ); + throw new RuntimeException(sprintf('Entity of type "%s" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?', get_class($object))); } $this->om->initializeObject($object); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 878e19ccb5..efc611859a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -630,6 +630,34 @@ class UniqueEntityValidatorTest extends ConstraintValidatorTestCase $this->validator->validate($entity, $constraint); } + public function testValidateUniquenessOnNullResult() + { + $repository = $this->createRepositoryMock(); + $repository + ->method('find') + ->will($this->returnValue(null)) + ; + + $this->em = $this->createEntityManagerMock($repository); + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $entity = new SingleIntIdEntity(1, null); + + $this->em->persist($entity); + $this->em->flush(); + + $this->validator->validate($entity, $constraint); + $this->assertNoViolation(); + } + public function testValidateInheritanceUniqueness() { $constraint = new UniqueEntity(array( diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index e03ef3c555..004de6f67e 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -148,15 +148,23 @@ class UniqueEntityValidator extends ConstraintValidator */ if ($result instanceof \Iterator) { $result->rewind(); - } elseif (is_array($result)) { + if ($result instanceof \Countable && 1 < \count($result)) { + $result = array($result->current(), $result->current()); + } else { + $result = $result->current(); + $result = null === $result ? array() : array($result); + } + } elseif (\is_array($result)) { reset($result); + } else { + $result = null === $result ? array() : array($result); } /* If no entity matched the query criteria or a single entity matched, * which is the same as the entity being validated, the criteria is * unique. */ - if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) { + if (!$result || (1 === \count($result) && current($result) === $entity)) { return; } diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 22992da680..29b1960798 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -297,17 +297,38 @@ class DeprecationErrorHandler }); } + /** + * Returns true if STDOUT is defined and supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool + */ private static function hasColorSupport() { - if ('\\' === DIRECTORY_SEPARATOR) { - return - defined('STDOUT') && function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD + if (!defined('STDOUT')) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + if (function_exists('stream_isatty')) { + return stream_isatty(STDOUT); + } + + if (function_exists('posix_isatty')) { + return posix_isatty(STDOUT); + } + + $stat = fstat(STDOUT); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index 37bb164ff6..5fc5d01e14 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -83,31 +83,34 @@ class StreamOutput extends Output * * Colorization is disabled if not supported by the stream: * - * - the stream is redirected (eg php file.php >log) - * - Windows without VT100 support, Ansicon, ConEmu, Mintty - * - non tty consoles + * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo + * terminals via named pipes, so we can only check the environment. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler * * @return bool true if the stream supports colorization, false otherwise */ protected function hasColorSupport() { - if (function_exists('stream_isatty') && !@stream_isatty($this->stream)) { - return false; - } if (DIRECTORY_SEPARATOR === '\\') { - if (function_exists('sapi_windows_vt100_support')) { - $vt100Enabled = @sapi_windows_vt100_support($this->stream); - } else { - $vt100Enabled = '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD; - } - - return - $vt100Enabled + return (function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($this->stream)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return function_exists('posix_isatty') && @posix_isatty($this->stream); + if (function_exists('stream_isatty')) { + return @stream_isatty($this->stream); + } + + if (function_exists('posix_isatty')) { + return @posix_isatty($this->stream); + } + + $stat = @fstat($this->stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 047190a38f..e05194b018 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -21,6 +21,7 @@ class Response const HTTP_CONTINUE = 100; const HTTP_SWITCHING_PROTOCOLS = 101; const HTTP_PROCESSING = 102; // RFC2518 + const HTTP_EARLY_HINTS = 103; // RFC8297 const HTTP_OK = 200; const HTTP_CREATED = 201; const HTTP_ACCEPTED = 202; diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 2e8007349a..d809331cb0 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -67,7 +67,7 @@ class DumpDataCollectorTest extends TestCase ob_start(); $collector->collect(new Request(), new Response()); - $output = ob_get_clean(); + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output); $this->assertSame(1, $collector->getDumpsCount()); @@ -111,7 +111,8 @@ EOTXT; ob_start(); $collector->__destruct(); - $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output); } public function testFlushNothingWhenDataDumperIsProvided() @@ -123,10 +124,11 @@ EOTXT; ob_start(); $collector->dump($data); $line = __LINE__ - 1; + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); if (\PHP_VERSION_ID >= 50400) { - $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output); } else { - $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", ob_get_clean()); + $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", $output); } ob_start(); diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 8343f03419..5c3475ed0e 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -144,7 +144,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property return; } - if (!in_array($prefix, $this->arrayMutatorPrefixes)) { + if (!\in_array($prefix, $this->arrayMutatorPrefixes)) { return $types; } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 69ec253b95..bc9ae5074d 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -194,7 +194,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return; } - if (in_array($prefix, $this->arrayMutatorPrefixes)) { + if (\in_array($prefix, $this->arrayMutatorPrefixes)) { $type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type); } @@ -220,7 +220,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp return array($this->extractFromReflectionType($reflectionType)); } - if (in_array($prefix, array('is', 'can'))) { + if (\in_array($prefix, array('is', 'can'))) { return array(new Type(Type::BUILTIN_TYPE_BOOL)); } } diff --git a/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php index a2e98d0feb..3f400a8fa5 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyDescriptionExtractorInterface.php @@ -12,7 +12,7 @@ namespace Symfony\Component\PropertyInfo; /** - * Description extractor Interface. + * Guesses the property's human readable description. * * @author Kévin Dunglas */ diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index 22e409c101..53881fb71a 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -99,7 +99,7 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface private function extract($extractors, $method, array $arguments) { foreach ($extractors as $extractor) { - $value = call_user_func_array(array($extractor, $method), $arguments); + $value = \call_user_func_array(array($extractor, $method), $arguments); if (null !== $value) { return $value; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php index ded0e94f50..22f3c4da85 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php @@ -54,7 +54,7 @@ class SimpleAuthenticationProvider implements AuthenticationProviderInterface $user = $this->userProvider->loadUserByUsername($user); if (!$user instanceof UserInterface) { - throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); + return $authToken; } } catch (UsernameNotFoundException $e) { $e->setUsername($user); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php index acee33b856..752c9d2aa4 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php @@ -12,10 +12,11 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Provider; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Exception\DisabledException; use Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider; +use Symfony\Component\Security\Core\Exception\DisabledException; use Symfony\Component\Security\Core\Exception\LockedException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\UserChecker; class SimpleAuthenticationProviderTest extends TestCase { @@ -121,6 +122,20 @@ class SimpleAuthenticationProviderTest extends TestCase $this->getProvider($authenticator, $userProvider)->authenticate($token); } + public function testAuthenticateSkipsUserChecksForNonUserInterfaceObjects() + { + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $token->expects($this->any()) + ->method('getUser') + ->will($this->returnValue('string-user')); + $authenticator = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface')->getMock(); + $authenticator->expects($this->once()) + ->method('authenticateToken') + ->will($this->returnValue($token)); + + $this->assertSame($token, $this->getProvider($authenticator, null, new UserChecker())->authenticate($token)); + } + protected function getProvider($simpleAuthenticator = null, $userProvider = null, $userChecker = null, $key = 'test') { if (null === $userChecker) { diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index 63b958a4b7..b388478f56 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -18,7 +18,6 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; @@ -118,11 +117,6 @@ class GuardAuthenticatorHandler */ public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey) { - $token = $this->tokenStorage->getToken(); - if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) { - $this->tokenStorage->setToken(null); - } - $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException); if ($response instanceof Response || null === $response) { // returning null is ok, it means they want the request to continue diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 2f2678035f..656736653e 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Guard\Provider; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Guard\AuthenticatorInterface; @@ -62,7 +63,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface */ public function authenticate(TokenInterface $token) { - if (!$this->supports($token)) { + if (!$token instanceof GuardTokenInterface) { throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.'); } @@ -86,19 +87,13 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface throw new AuthenticationExpiredException(); } - // find the *one* GuardAuthenticator that this token originated from - foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { - // get a key that's unique to *this* guard authenticator - // this MUST be the same as GuardAuthenticationListener - $uniqueGuardKey = $this->providerKey.'_'.$key; + $guardAuthenticator = $this->findOriginatingAuthenticator($token); - if ($uniqueGuardKey == $token->getGuardProviderKey()) { - return $this->authenticateViaGuard($guardAuthenticator, $token); - } + if (null === $guardAuthenticator) { + throw new AuthenticationException(sprintf('Token with provider key "%s" did not originate from any of the guard authenticators of provider "%s".', $token->getGuardProviderKey(), $this->providerKey)); } - // no matching authenticator found - but there will be multiple GuardAuthenticationProvider - // instances that will be checked if you have multiple firewalls. + return $this->authenticateViaGuard($guardAuthenticator, $token); } private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token) @@ -107,18 +102,11 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); if (null === $user) { - throw new UsernameNotFoundException(sprintf( - 'Null returned from %s::getUser()', - get_class($guardAuthenticator) - )); + throw new UsernameNotFoundException(sprintf('Null returned from %s::getUser()', get_class($guardAuthenticator))); } if (!$user instanceof UserInterface) { - throw new \UnexpectedValueException(sprintf( - 'The %s::getUser() method must return a UserInterface. You returned %s.', - get_class($guardAuthenticator), - is_object($user) ? get_class($user) : gettype($user) - )); + throw new \UnexpectedValueException(sprintf('The %s::getUser() method must return a UserInterface. You returned %s.', get_class($guardAuthenticator), is_object($user) ? get_class($user) : gettype($user))); } $this->userChecker->checkPreAuth($user); @@ -130,18 +118,37 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface // turn the UserInterface into a TokenInterface $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey); if (!$authenticatedToken instanceof TokenInterface) { - throw new \UnexpectedValueException(sprintf( - 'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', - get_class($guardAuthenticator), - is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken) - )); + throw new \UnexpectedValueException(sprintf('The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', get_class($guardAuthenticator), is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken))); } return $authenticatedToken; } + private function findOriginatingAuthenticator(PreAuthenticationGuardToken $token) + { + // find the *one* GuardAuthenticator that this token originated from + foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { + // get a key that's unique to *this* guard authenticator + // this MUST be the same as GuardAuthenticationListener + $uniqueGuardKey = $this->providerKey.'_'.$key; + + if ($uniqueGuardKey === $token->getGuardProviderKey()) { + return $guardAuthenticator; + } + } + + // no matching authenticator found - but there will be multiple GuardAuthenticationProvider + // instances that will be checked if you have multiple firewalls. + + return null; + } + public function supports(TokenInterface $token) { + if ($token instanceof PreAuthenticationGuardToken) { + return null !== $this->findOriginatingAuthenticator($token); + } + return $token instanceof GuardTokenInterface; } } diff --git a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php index c67f38e9ef..5b03d4e138 100644 --- a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php @@ -82,7 +82,7 @@ class GuardAuthenticatorHandlerTest extends TestCase /** * @dataProvider getTokenClearingTests */ - public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared) + public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey) { $token = $this->getMockBuilder($tokenClass) ->disableOriginalConstructor() @@ -91,12 +91,7 @@ class GuardAuthenticatorHandlerTest extends TestCase ->method('getProviderKey') ->will($this->returnValue($tokenProviderKey)); - // make the $token be the current token - $this->tokenStorage->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never()) + $this->tokenStorage->expects($this->never()) ->method('setToken') ->with(null); $authException = new AuthenticationException('Bad password!'); @@ -116,9 +111,9 @@ class GuardAuthenticatorHandlerTest extends TestCase { $tests = array(); // correct token class and matching firewall => clear the token - $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true); - $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false); - $tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false); + $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key'); + $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key'); + $tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key'); return $tests; } diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index 9d8301fce8..17ea596d15 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -17,6 +17,7 @@ use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; /** * @author Ryan Weaver @@ -198,6 +199,40 @@ class GuardAuthenticationProviderTest extends TestCase $actualToken = $provider->authenticate($token); } + public function testSupportsChecksGuardAuthenticatorsTokenOrigin() + { + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticators = array($authenticatorA, $authenticatorB); + + $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker); + + $token = new PreAuthenticationGuardToken($mockedUser, 'first_firewall_1'); + $supports = $provider->supports($token); + $this->assertTrue($supports); + + $token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0'); + $supports = $provider->supports($token); + $this->assertFalse($supports); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessageRegExp /second_firewall_0/ + */ + public function testAuthenticateFailsOnNonOriginatingToken() + { + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticators = array($authenticatorA); + + $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker); + + $token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0'); + $provider->authenticate($token); + } + protected function setUp() { $this->userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock(); diff --git a/src/Symfony/Component/VarDumper/Caster/SplCaster.php b/src/Symfony/Component/VarDumper/Caster/SplCaster.php index c9d25feeb9..e8be74d604 100644 --- a/src/Symfony/Component/VarDumper/Caster/SplCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SplCaster.php @@ -184,10 +184,11 @@ class SplCaster $storage = array(); unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 - foreach (clone $c as $obj) { + $clone = clone $c; + foreach ($clone as $obj) { $storage[spl_object_hash($obj)] = array( 'object' => $obj, - 'info' => $c->getInfo(), + 'info' => $clone->getInfo(), ); } diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 30c2531191..f1de350d28 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -62,7 +62,7 @@ class CliDumper extends AbstractDumper { parent::__construct($output, $charset, $flags); - if ('\\' === DIRECTORY_SEPARATOR && 'ON' !== @getenv('ConEmuANSI') && 'xterm' !== @getenv('TERM')) { + if ('\\' === DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles(array( 'default' => '31', @@ -467,7 +467,7 @@ class CliDumper extends AbstractDumper protected function supportsColors() { if ($this->outputStream !== static::$defaultOutput) { - return @(is_resource($this->outputStream) && function_exists('posix_isatty') && posix_isatty($this->outputStream)); + return $this->hasColorSupport($this->outputStream); } if (null !== static::$defaultColors) { return static::$defaultColors; @@ -495,23 +495,10 @@ class CliDumper extends AbstractDumper } } - if ('\\' === DIRECTORY_SEPARATOR) { - static::$defaultColors = @( - function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support($this->outputStream) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD - || false !== getenv('ANSICON') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM') - ); - } elseif (function_exists('posix_isatty')) { - $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); - $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - static::$defaultColors = @posix_isatty($h); - } else { - static::$defaultColors = false; - } + $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); + $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - return static::$defaultColors; + return static::$defaultColors = $this->hasColorSupport($h); } /** @@ -537,4 +524,69 @@ class CliDumper extends AbstractDumper $this->dumpLine($cursor->depth, true); } + + /** + * Returns true if the stream supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @param mixed $stream A CLI output stream + * + * @return bool + */ + private function hasColorSupport($stream) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($stream)) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + if (function_exists('stream_isatty')) { + return @stream_isatty($stream); + } + + if (function_exists('posix_isatty')) { + return @posix_isatty($stream); + } + + $stat = @fstat($stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + /** + * Returns true if the Windows terminal supports true color. + * + * Note that this does not check an output stream, but relies on environment + * variables from known implementations, or a PHP and Windows version that + * supports true color. + * + * @return bool + */ + private function isWindowsTrueColor() + { + $result = 183 <= getenv('ANSICON_VER') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + + if (!$result && PHP_VERSION_ID >= 70200) { + $version = sprintf( + '%s.%s.%s', + PHP_WINDOWS_VERSION_MAJOR, + PHP_WINDOWS_VERSION_MINOR, + PHP_WINDOWS_VERSION_BUILD + ); + $result = $version >= '10.0.15063'; + } + + return $result; + } } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php index e2181d90b5..48531562de 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php @@ -144,4 +144,23 @@ EOTXT; array(\SplDoublyLinkedList::IT_MODE_LIFO | \SplDoublyLinkedList::IT_MODE_DELETE, 'IT_MODE_LIFO | IT_MODE_DELETE'), ); } + + public function testCastObjectStorageIsntModified() + { + $var = new \SplObjectStorage(); + $var->attach(new \stdClass()); + $var->rewind(); + $current = $var->current(); + + $this->assertDumpMatchesFormat('%A', $var); + $this->assertSame($current, $var->current()); + } + + public function testCastObjectStorageDumpsInfo() + { + $var = new \SplObjectStorage(); + $var->attach(new \stdClass(), new \DateTime()); + + $this->assertDumpMatchesFormat('%ADateTime%A', $var); + } }