diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index 4dae84f641..db4a2fa37a 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -239,7 +239,7 @@ class OutputFormatter implements OutputFormatterInterface * * @param string $text Input text * - * @return string string Styled text + * @return string Styled text */ private function applyCurrentStyle($text) { diff --git a/src/Symfony/Component/Finder/Iterator/FilterIterator.php b/src/Symfony/Component/Finder/Iterator/FilterIterator.php index 64da508ebd..f4da44c4cd 100644 --- a/src/Symfony/Component/Finder/Iterator/FilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/FilterIterator.php @@ -30,7 +30,14 @@ abstract class FilterIterator extends \FilterIterator { $iterator = $this; while ($iterator instanceof \OuterIterator) { - if ($iterator->getInnerIterator() instanceof \FilesystemIterator) { + $innerIterator = $iterator->getInnerIterator(); + + if ($innerIterator instanceof RecursiveDirectoryIterator) { + if ($innerIterator->isRewindable()) { + $innerIterator->next(); + $innerIterator->rewind(); + } + } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) { $iterator->getInnerIterator()->next(); $iterator->getInnerIterator()->rewind(); } diff --git a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php index d08f644088..f189712dfd 100644 --- a/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php +++ b/src/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php @@ -26,6 +26,11 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator */ private $ignoreUnreadableDirs; + /** + * @var Boolean + */ + private $rewindable; + /** * Constructor. * @@ -73,4 +78,42 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator } } } + + /** + * Do nothing for non rewindable stream + */ + public function rewind() + { + if (false === $this->isRewindable()) { + return; + } + + // @see https://bugs.php.net/bug.php?id=49104 + parent::next(); + + parent::rewind(); + } + + /** + * Checks if the stream is rewindable. + * + * @return Boolean true when the stream is rewindable, false otherwise + */ + public function isRewindable() + { + if (null !== $this->rewindable) { + return $this->rewindable; + } + + if (false !== $stream = @opendir($this->getPath())) { + $infos = stream_get_meta_data($stream); + closedir($stream); + + if ($infos['seekable']) { + return $this->rewindable = true; + } + } + + return $this->rewindable = false; + } } diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 3914fe02b5..3c985d1fea 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -796,4 +796,47 @@ class FinderTest extends Iterator\RealIteratorTestCase } ); } + + /** + * Searching in multiple locations with sub directories involves + * AppendIterator which does an unnecessary rewind which leaves + * FilterIterator with inner FilesystemIterator in an ivalid state. + * + * @see https://bugs.php.net/bug.php?id=49104 + */ + public function testMultipleLocationsWithSubDirectories() + { + $locations = array( + __DIR__.'/Fixtures/one', + self::$files[8], + ); + + $finder = new Finder(); + $finder->in($locations)->depth('< 10')->name('*.neon'); + + $expected = array( + __DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'c.neon', + __DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'d.neon', + ); + + $this->assertIterator($expected, $finder); + $this->assertIteratorInForeach($expected, $finder); + } + + public function testNonSeekableStream() + { + try { + $i = Finder::create()->in('ftp://ftp.mozilla.org/')->depth(0)->getIterator(); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', 'ftp')); + } + + $contains = array( + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub', + ); + + $this->assertIteratorInForeach($contains, $i); + } } diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/one/a b/src/Symfony/Component/Finder/Tests/Fixtures/one/a new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/one/b/c.neon b/src/Symfony/Component/Finder/Tests/Fixtures/one/b/c.neon new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Symfony/Component/Finder/Tests/Fixtures/one/b/d.neon b/src/Symfony/Component/Finder/Tests/Fixtures/one/b/d.neon new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php b/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php index 7b3f8934e8..504cfb01b1 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/IteratorTestCase.php @@ -33,4 +33,41 @@ abstract class IteratorTestCase extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, array_values($values)); } + + /** + * Same as IteratorTestCase::assertIterator with foreach usage + * + * @param array $expected + * @param \Traversable $iterator + */ + protected function assertIteratorInForeach($expected, \Traversable $iterator) + { + $values = array(); + foreach ($iterator as $file) { + $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file); + $values[] = $file->getPathname(); + } + + sort($values); + sort($expected); + + $this->assertEquals($expected, array_values($values)); + } + + /** + * Same as IteratorTestCase::assertOrderedIterator with foreach usage + * + * @param array $expected + * @param \Traversable $iterator + */ + protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator) + { + $values = array(); + foreach ($iterator as $file) { + $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file); + $values[] = $file->getPathname(); + } + + $this->assertEquals($expected, array_values($values)); + } } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php new file mode 100644 index 0000000000..f762514346 --- /dev/null +++ b/src/Symfony/Component/Finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php @@ -0,0 +1,83 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Symfony\Component\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; + +class RecursiveDirectoryIteratorTest extends IteratorTestCase +{ + /** + * @dataProvider getPaths + * + * @param string $path + * @param Boolean $seekable + * @param Boolean $supports + * @param string $message + */ + public function testRewind($path, $seekable, $contains, $message = null) + { + try { + $i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', $path)); + } + + $i->rewind(); + + $this->assertTrue(true, $message); + } + + /** + * @dataProvider getPaths + * + * @param string $path + * @param Boolean $seekable + * @param Boolean $supports + * @param string $message + */ + public function testSeek($path, $seekable, $contains, $message = null) + { + try { + $i = new RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped(sprintf('Unsupported stream "%s".', $path)); + } + + $actual = array(); + + $i->seek(0); + $actual[] = $i->getPathname(); + + $i->seek(1); + $actual[] = $i->getPathname(); + + $i->seek(2); + $actual[] = $i->getPathname(); + + $this->assertEquals($contains, $actual); + } + + public function getPaths() + { + $data = array(); + + // ftp + $contains = array( + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html', + 'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub', + ); + $data[] = array('ftp://ftp.mozilla.org/', false, $contains); + + return $data; + } +} diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 3f37366a84..63cde7e5b2 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -194,8 +194,6 @@ class HttpCache implements HttpKernelInterface, TerminableInterface $response = $this->lookup($request, $catch); } - $response->isNotModified($request); - $this->restoreResponseBody($request, $response); $response->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); @@ -214,6 +212,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface $response->prepare($request); + $response->isNotModified($request); + return $response; } @@ -265,6 +265,15 @@ class HttpCache implements HttpKernelInterface, TerminableInterface try { $this->store->invalidate($request, $catch); + // As per the RFC, invalidate Location and Content-Location URLs if present + foreach (array('Location', 'Content-Location') as $header) { + if ($uri = $response->headers->get($header)) { + $subRequest = $request::create($uri, 'get', array(), array(), array(), $request->server->all()); + + $this->store->invalidate($subRequest); + } + } + $this->record($request, 'invalidate'); } catch (\Exception $e) { $this->record($request, 'invalidate-failed'); diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index 6143f0ba94..a1cda1fd27 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -246,15 +246,6 @@ class Store implements StoreInterface throw new \RuntimeException('Unable to store the metadata.'); } } - - // As per the RFC, invalidate Location and Content-Location URLs if present - foreach (array('Location', 'Content-Location') as $header) { - if ($uri = $request->headers->get($header)) { - $subRequest = $request::create($uri, 'get', array(), array(), array(), $request->server->all()); - - $this->invalidate($subRequest); - } - } } /** diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 448ebc362b..a8064b832b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -145,7 +145,7 @@ class HttpCacheTest extends HttpCacheTestCase $this->assertHttpKernelIsCalled(); $this->assertEquals(304, $this->response->getStatusCode()); - $this->assertEquals('text/html; charset=UTF-8', $this->response->headers->get('Content-Type')); + $this->assertEquals('', $this->response->headers->get('Content-Type')); $this->assertEmpty($this->response->getContent()); $this->assertTraceContains('miss'); $this->assertTraceContains('store'); @@ -158,7 +158,7 @@ class HttpCacheTest extends HttpCacheTestCase $this->assertHttpKernelIsCalled(); $this->assertEquals(304, $this->response->getStatusCode()); - $this->assertEquals('text/html; charset=UTF-8', $this->response->headers->get('Content-Type')); + $this->assertEquals('', $this->response->headers->get('Content-Type')); $this->assertTrue($this->response->headers->has('ETag')); $this->assertEmpty($this->response->getContent()); $this->assertTraceContains('miss'); @@ -845,7 +845,7 @@ class HttpCacheTest extends HttpCacheTestCase $this->assertTraceContains('fresh'); // now POST to same URL - $this->request('POST', '/'); + $this->request('POST', '/helloworld'); $this->assertHttpKernelIsCalled(); $this->assertEquals('/', $this->response->headers->get('Location')); $this->assertTraceContains('invalidate'); diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php index 1fc93e50cc..0840c1c622 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php @@ -38,7 +38,7 @@ interface VoterInterface * * @param string $class A class name * - * @return true if this Voter can process the class + * @return Boolean true if this Voter can process the class */ public function supportsClass($class);