From 342058742c6b8e7f2aca77525c766439cefecfca Mon Sep 17 00:00:00 2001 From: Hamish Friedlander Date: Thu, 1 Aug 2013 09:44:36 +1200 Subject: [PATCH 1/4] FIX Flush on memory exhaustion and headers sent --- core/startup/ErrorControlChain.php | 24 ++++++++++++ core/startup/ParameterConfirmationToken.php | 9 ++++- tests/core/startup/ErrorControlChainTest.php | 41 ++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/core/startup/ErrorControlChain.php b/core/startup/ErrorControlChain.php index dc32257e7..4ac510cbb 100644 --- a/core/startup/ErrorControlChain.php +++ b/core/startup/ErrorControlChain.php @@ -74,9 +74,33 @@ class ErrorControlChain { return $error && ($error['type'] & self::$fatal_errors) != 0; } + protected function lastErrorWasMemoryExhaustion() { + $error = error_get_last(); + $message = $error ? $error['message'] : ''; + return stripos($message, 'memory') !== false && stripos($message, 'exhausted') !== false; + } + + static $transtable = array( + 'k' => 1024, + 'm' => 1048576, + 'g' => 1073741824 + ); + + protected function translateMemstring($memString) { + $char = strtolower(substr($memString, -1)); + $fact = isset(self::$transtable[$char]) ? self::$transtable[$char] : 1; + return ((int)$memString) * $fact; + } + public function handleFatalError() { if ($this->handleFatalErrors && $this->suppression) { if ($this->lastErrorWasFatal()) { + if ($this->lastErrorWasMemoryExhaustion()) { + // Bump up memory limit by an arbitrary 10% / 10MB (whichever is bigger) since we've run out + $cur = $this->translateMemstring(ini_get('memory_limit')); + if ($cur != -1) ini_set('memory_limit', $cur + max(round($cur*0.1), 10000000)); + } + $this->error = true; $this->step(); } diff --git a/core/startup/ParameterConfirmationToken.php b/core/startup/ParameterConfirmationToken.php index 21364f871..fe85d293f 100644 --- a/core/startup/ParameterConfirmationToken.php +++ b/core/startup/ParameterConfirmationToken.php @@ -94,7 +94,14 @@ class ParameterConfirmationToken { $location = "$proto://" . $host . BASE_URL . $url . ($params ? '?'.http_build_query($params) : ''); // And redirect - header('location: '.$location, true, 302); + if (headers_sent()) { + echo " + + +You are being redirected. If you are not redirected soon, click here to continue the flush +"; + } + else header('location: '.$location, true, 302); die; } } diff --git a/tests/core/startup/ErrorControlChainTest.php b/tests/core/startup/ErrorControlChainTest.php index 8b5a1d622..b715bbe3b 100644 --- a/tests/core/startup/ErrorControlChainTest.php +++ b/tests/core/startup/ErrorControlChainTest.php @@ -8,6 +8,11 @@ */ class ErrorControlChainTest_Chain extends ErrorControlChain { + // Change function visibility to be testable directly + public function translateMemstring($memstring) { + return parent::translateMemstring($memstring); + } + function executeInSubprocess() { // Get the path to the ErrorControlChain class $classpath = SS_ClassLoader::instance()->getItemPath('ErrorControlChain'); @@ -114,6 +119,23 @@ class ErrorControlChainTest extends SapphireTest { ->executeInSubprocess(); $this->assertEquals('Done', $out); + + // Memory exhaustion + + $chain = new ErrorControlChainTest_Chain(); + + list($out, $code) = $chain + ->then(function(){ + ini_set('memory_limit', '10M'); + $a = array(); + while(1) $a[] = 1; + }) + ->thenIfErrored(function(){ + echo "Done"; + }) + ->executeInSubprocess(); + + $this->assertEquals('Done', $out); } function testExceptionSuppression() { @@ -238,4 +260,23 @@ class ErrorControlChainTest extends SapphireTest { $this->assertContains("Good", $out); } + + function testMemoryConversion() { + $chain = new ErrorControlChainTest_Chain(); + + $this->assertEquals(200, $chain->translateMemstring('200')); + $this->assertEquals(300, $chain->translateMemstring('300')); + + $this->assertEquals(2 * 1024, $chain->translateMemstring('2k')); + $this->assertEquals(3 * 1024, $chain->translateMemstring('3K')); + + $this->assertEquals(2 * 1024 * 1024, $chain->translateMemstring('2m')); + $this->assertEquals(3 * 1024 * 1024, $chain->translateMemstring('3M')); + + $this->assertEquals(2 * 1024 * 1024 * 1024, $chain->translateMemstring('2g')); + $this->assertEquals(3 * 1024 * 1024 * 1024, $chain->translateMemstring('3G')); + + $this->assertEquals(200, $chain->translateMemstring('200foo')); + $this->assertEquals(300, $chain->translateMemstring('300foo')); + } } \ No newline at end of file From a685a8dee989727a73ac3bf377d667a79a834469 Mon Sep 17 00:00:00 2001 From: Hamish Friedlander Date: Fri, 2 Aug 2013 11:00:26 +1200 Subject: [PATCH 2/4] FIX Include flushtoken when install redirects to successfullyinstalled --- dev/install/install.php5 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dev/install/install.php5 b/dev/install/install.php5 index 890250e7c..33c124d67 100644 --- a/dev/install/install.php5 +++ b/dev/install/install.php5 @@ -1222,8 +1222,12 @@ PHP $this->statusMessage("Checking that friendly URLs work..."); $this->checkRewrite(); } else { + require_once 'core/startup/ParameterConfirmationToken.php'; + $token = new ParameterConfirmationToken('flush'); + $params = http_build_query($token->params()); + $destinationURL = 'index.php/' . - ($this->checkModuleExists('cms') ? 'home/successfullyinstalled?flush=1' : '?flush=1'); + ($this->checkModuleExists('cms') ? "home/successfullyinstalled?$params" : "?$params"); echo <<SilverStripe successfully installed; I am now redirecting you to your SilverStripe site... @@ -1361,8 +1365,12 @@ TEXT; } function checkRewrite() { + require_once 'core/startup/ParameterConfirmationToken.php'; + $token = new ParameterConfirmationToken('flush'); + $params = http_build_query($token->params()); + $destinationURL = str_replace('install.php', '', $_SERVER['SCRIPT_NAME']) . - ($this->checkModuleExists('cms') ? 'home/successfullyinstalled?flush=1' : '?flush=1'); + ($this->checkModuleExists('cms') ? "home/successfullyinstalled?$params" : "?$params"); echo <<Testing... From 041466fe024ade10f085f247e67d6f459a9f48ed Mon Sep 17 00:00:00 2001 From: Hamish Friedlander Date: Mon, 5 Aug 2013 09:15:11 +1200 Subject: [PATCH 3/4] FIX Token redirect where in IIS a / needs adding between host & url --- core/startup/ParameterConfirmationToken.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/startup/ParameterConfirmationToken.php b/core/startup/ParameterConfirmationToken.php index fe85d293f..288488b6b 100644 --- a/core/startup/ParameterConfirmationToken.php +++ b/core/startup/ParameterConfirmationToken.php @@ -91,7 +91,7 @@ class ParameterConfirmationToken { unset($params['url']); // Join them all together into the original URL - $location = "$proto://" . $host . BASE_URL . $url . ($params ? '?'.http_build_query($params) : ''); + $location = "$proto://" . $host . '/' . ltrim(BASE_URL, '/') . $url . ($params ? '?'.http_build_query($params) : ''); // And redirect if (headers_sent()) { From 5f9387c42cdcda9b906e39751c35660f8bdf3738 Mon Sep 17 00:00:00 2001 From: Hamish Friedlander Date: Mon, 5 Aug 2013 14:58:44 +1200 Subject: [PATCH 4/4] FIX Constants magic_quotes handling needs function from Core --- core/Constants.php | 7 +++++++ core/Core.php | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/Constants.php b/core/Constants.php index cded92297..b02c31c20 100644 --- a/core/Constants.php +++ b/core/Constants.php @@ -54,6 +54,13 @@ do { /////////////////////////////////////////////////////////////////////////////// // GLOBALS AND DEFINE SETTING +function stripslashes_recursively(&$array) { + foreach($array as $k => $v) { + if(is_array($v)) stripslashes_recursively($array[$k]); + else $array[$k] = stripslashes($v); + } +} + /** * A blank HTTP_HOST value is used to detect command-line execution. * We update the $_SERVER variable to contain data consistent with the rest of the application. diff --git a/core/Core.php b/core/Core.php index 9071c0557..5347439b3 100644 --- a/core/Core.php +++ b/core/Core.php @@ -164,13 +164,6 @@ function project() { return $project; } -function stripslashes_recursively(&$array) { - foreach($array as $k => $v) { - if(is_array($v)) stripslashes_recursively($array[$k]); - else $array[$k] = stripslashes($v); - } -} - /** * @see i18n::_t() */