From 10dea948f38a684336fa965176529917ff7fe533 Mon Sep 17 00:00:00 2001 From: Joseph Bielawski Date: Sun, 28 Apr 2013 18:32:44 +0300 Subject: [PATCH 1/3] [Filesystem] copy() is not working when open_basedir is set More details: https://bugs.php.net/bug.php?id=64634 --- src/Symfony/Component/Filesystem/Filesystem.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 778bd236f3..c3050423c1 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -35,6 +35,10 @@ class Filesystem */ public function copy($originFile, $targetFile, $override = false) { + if (!is_file($originFile)) { + throw new IOException(sprintf('Failed to copy %s because file not exists', $originFile)); + } + $this->mkdir(dirname($targetFile)); if (!$override && is_file($targetFile)) { @@ -44,7 +48,15 @@ class Filesystem } if ($doCopy) { - if (true !== @copy($originFile, $targetFile)) { + // https://bugs.php.net/bug.php?id=64634 + $source = fopen($originFile, 'r'); + $target = fopen($targetFile, 'w+'); + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile)); } } From fbe039bfca96c97da76be07f3a49923dd35d79af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Bj=C3=B8rnskov?= Date: Tue, 30 Apr 2013 13:43:48 +0200 Subject: [PATCH 2/3] use while loop for iterating The other method limits the number of results without taking the constraints into account. Fixes GH-7883 --- .../HttpKernel/Profiler/FileProfilerStorage.php | 6 +----- .../Profiler/AbstractProfilerStorageTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php index e2e259194d..7adde3f3c3 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -58,10 +58,7 @@ class FileProfilerStorage implements ProfilerStorageInterface fseek($file, 0, SEEK_END); $result = array(); - - while ($limit > 0) { - $line = $this->readLineFromFile($file); - + while (count($result) < $limit && $line = $this->readLineFromFile($file)) { if (false === $line) { break; } @@ -84,7 +81,6 @@ class FileProfilerStorage implements ProfilerStorageInterface 'time' => $csvTime, 'parent' => $csvParent, ); - --$limit; } fclose($file); diff --git a/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php b/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php index 62f24ce87a..e3f024eef3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Profiler/AbstractProfilerStorageTest.php @@ -183,6 +183,21 @@ abstract class AbstractProfilerStorageTest extends \PHPUnit_Framework_TestCase $this->getStorage()->purge(); } + public function testRetrieveByMethodAndLimit() + { + foreach (array('POST', 'GET') as $method) { + for ($i = 0; $i < 5; $i++) { + $profile = new Profile('token_'.$i.$method); + $profile->setMethod($method); + $this->getStorage()->write($profile); + } + } + + $this->assertCount(5, $this->getStorage()->find('', '', 5, 'POST')); + + $this->getStorage()->purge(); + } + public function testPurge() { $profile = new Profile('token1'); From 2b554d77fa882ffa4abe5c884197782254c580b5 Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Fri, 26 Apr 2013 14:05:25 +0200 Subject: [PATCH 3/3] remove validation related headers when needed --- .../HttpCache/EsiResponseCacheStrategy.php | 27 ++++++++++++------ .../HttpKernel/HttpCache/HttpCache.php | 4 +-- .../Tests/HttpCache/HttpCacheTest.php | 28 +++++++++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php b/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php index c5ec81051f..6384af9660 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php @@ -29,13 +29,12 @@ use Symfony\Component\HttpFoundation\Response; class EsiResponseCacheStrategy implements EsiResponseCacheStrategyInterface { private $cacheable = true; + private $embeddedResponses = 0; private $ttls = array(); private $maxAges = array(); /** - * Adds a Response. - * - * @param Response $response + * {@inheritdoc} */ public function add(Response $response) { @@ -45,26 +44,38 @@ class EsiResponseCacheStrategy implements EsiResponseCacheStrategyInterface $this->ttls[] = $response->getTtl(); $this->maxAges[] = $response->getMaxAge(); } + + $this->embeddedResponses++; } /** - * Updates the Response HTTP headers based on the embedded Responses. - * - * @param Response $response + * {@inheritdoc} */ public function update(Response $response) { - // if we only have one Response, do nothing - if (1 === count($this->ttls)) { + // if we have no embedded Response, do nothing + if (0 === $this->embeddedResponses) { return; } + // Remove validation related headers in order to avoid browsers using + // their own cache, because some of the response content comes from + // at least one embedded response (which likely has a different caching strategy). + if ($response->isValidateable()) { + $response->setEtag(null); + $response->setLastModified(null); + $this->cacheable = false; + } + if (!$this->cacheable) { $response->headers->set('Cache-Control', 'no-cache, must-revalidate'); return; } + $this->ttls[] = $response->getTtl(); + $this->maxAges[] = $response->getMaxAge(); + if (null !== $maxAge = min($this->maxAges)) { $response->setSharedMaxAge($maxAge); $response->headers->set('Age', $maxAge - min($this->ttls)); diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index ae918e1543..2592d3b171 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -204,10 +204,10 @@ class HttpCache implements HttpKernelInterface, TerminableInterface } if (null !== $this->esi) { - $this->esiCacheStrategy->add($response); - if (HttpKernelInterface::MASTER_REQUEST === $type) { $this->esiCacheStrategy->update($response); + } else { + $this->esiCacheStrategy->add($response); } } diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 4cdd5f60e1..448ebc362b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -1075,4 +1075,32 @@ class HttpCacheTest extends HttpCacheTestCase $this->assertEquals('10.0.0.1', $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); } + + public function testEsiCacheRemoveValidationHeadersIfEmbeddedResponses() + { + $time = new \DateTime; + + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Surrogate-Control' => 'content="ESI/1.0"', + 'ETag' => 'hey', + 'Last-Modified' => $time->format(DATE_RFC2822), + ), + ), + array( + 'status' => 200, + 'body' => 'Hey!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertNull($this->response->getETag()); + $this->assertNull($this->response->getLastModified()); + } }