Issue #2003800 by jhedstrom, filijonka, YesCT, ParisLiakos, steeloctopus, damiankloip, sun, LinL, amateescu: Move drupal_check_memory_limit() and parse_size() functionality to components.

8.0.x
Alex Pott 2014-05-23 20:25:52 +01:00
parent 9f92f4e064
commit eed52b4d82
14 changed files with 282 additions and 123 deletions

View File

@ -5,6 +5,7 @@
*/
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Environment;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Timer;
@ -158,13 +159,6 @@ const DRUPAL_ANONYMOUS_RID = 'anonymous';
*/
const DRUPAL_AUTHENTICATED_RID = 'authenticated';
/**
* The number of bytes in a kilobyte.
*
* For more information, visit http://en.wikipedia.org/wiki/Kilobyte.
*/
const DRUPAL_KILOBYTE = 1024;
/**
* The maximum number of characters in a module or theme name.
*/
@ -2294,18 +2288,12 @@ function _drupal_shutdown_function() {
* @return
* TRUE if there is sufficient memory to allow the operation, or FALSE
* otherwise.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal\Component\Utility\Environment::checkMemoryLimit().
*/
function drupal_check_memory_limit($required, $memory_limit = NULL) {
if (!isset($memory_limit)) {
$memory_limit = ini_get('memory_limit');
}
// There is sufficient memory if:
// - No memory limit is set.
// - The memory limit is set to unlimited (-1).
// - The memory limit is greater than or equal to the memory required for
// the operation.
return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
return Environment::checkMemoryLimit($required, $memory_limit);
}
/**

View File

@ -11,6 +11,7 @@
use Drupal\Component\Serialization\Json;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\SortArray;
@ -549,17 +550,12 @@ function format_plural($count, $singular, $plural, array $args = array(), array
*
* @return
* An integer representation of the size in bytes.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal\Component\Utility\Bytes::toInt().
*/
function parse_size($size) {
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
if ($unit) {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round($size * pow(DRUPAL_KILOBYTE, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
}
return Bytes::toInt($size);
}
/**
@ -575,11 +571,11 @@ function parse_size($size) {
* A translated string representation of the size.
*/
function format_size($size, $langcode = NULL) {
if ($size < DRUPAL_KILOBYTE) {
if ($size < Bytes::KILOBYTE) {
return format_plural($size, '1 byte', '@count bytes', array(), array('langcode' => $langcode));
}
else {
$size = $size / DRUPAL_KILOBYTE; // Convert bytes to kilobytes.
$size = $size / Bytes::KILOBYTE; // Convert bytes to kilobytes.
$units = array(
t('@size KB', array(), array('langcode' => $langcode)),
t('@size MB', array(), array('langcode' => $langcode)),
@ -591,8 +587,8 @@ function format_size($size, $langcode = NULL) {
t('@size YB', array(), array('langcode' => $langcode)),
);
foreach ($units as $unit) {
if (round($size, 2) >= DRUPAL_KILOBYTE) {
$size = $size / DRUPAL_KILOBYTE;
if (round($size, 2) >= Bytes::KILOBYTE) {
$size = $size / Bytes::KILOBYTE;
}
else {
break;

View File

@ -8,6 +8,7 @@
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\StreamWrapper\LocalStream;
use Drupal\Component\PhpStorage\FileStorage;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\String;
use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PublicStream;
@ -1271,11 +1272,11 @@ function file_upload_max_size() {
if ($max_size < 0) {
// Start with post_max_size.
$max_size = parse_size(ini_get('post_max_size'));
$max_size = Bytes::toInt(ini_get('post_max_size'));
// If upload_max_size is less, then reduce. Except if upload_max_size is
// zero, which indicates no limit.
$upload_max = parse_size(ini_get('upload_max_filesize'));
$upload_max = Bytes::toInt(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = $upload_max;
}

View File

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \Drupal\Component\Utility\Bytes.
*/
namespace Drupal\Component\Utility;
/**
* Provides helper methods for byte conversions.
*/
class Bytes {
/**
* The number of bytes in a kilobyte.
*
* @see http://en.wikipedia.org/wiki/Kilobyte
*/
const KILOBYTE = 1024;
/**
* Parses a given byte size.
*
* @param mixed $size
* An integer or string size expressed as a number of bytes with optional SI
* or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
*
* @return int
* An integer representation of the size in bytes.
*/
public static function toInt($size) {
// Remove the non-unit characters from the size.
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
// Remove the non-numeric characters from the size.
$size = preg_replace('/[^0-9\.]/', '', $size);
if ($unit) {
// Find the position of the unit in the ordered string which is the power
// of magnitude to multiply a kilobyte by.
return round($size * pow(self::KILOBYTE, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
}
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Component\Utility\Environment.
*/
namespace Drupal\Component\Utility;
/**
* Provides PHP environment helper methods.
*/
class Environment {
/**
* Compares the memory required for an operation to the available memory.
*
* @param string $required
* The memory required for the operation, expressed as a number of bytes with
* optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8bytes,
* 9mbytes).
* @param $memory_limit
* (optional) The memory limit for the operation, expressed as a number of
* bytes with optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G,
* 6GiB, 8bytes, 9mbytes). If no value is passed, the current PHP
* memory_limit will be used. Defaults to NULL.
*
* @return bool
* TRUE if there is sufficient memory to allow the operation, or FALSE
* otherwise.
*/
public static function checkMemoryLimit($required, $memory_limit = NULL) {
if (!isset($memory_limit)) {
$memory_limit = ini_get('memory_limit');
}
// There is sufficient memory if:
// - No memory limit is set.
// - The memory limit is set to unlimited (-1).
// - The memory limit is greater than or equal to the memory required for
// the operation.
return ((!$memory_limit) || ($memory_limit == -1) || (Bytes::toInt($memory_limit) >= Bytes::toInt($required)));
}
}

View File

@ -5,6 +5,8 @@
*/
use Drupal\Core\Asset\CssOptimizer;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Environment;
use Drupal\Component\Utility\String;
use Symfony\Component\HttpFoundation\Request;
@ -378,8 +380,8 @@ function color_scheme_form_submit($form, &$form_state) {
// scheme change based on a faulty memory calculation.
$usage = memory_get_usage(TRUE);
$memory_limit = ini_get('memory_limit');
$size = parse_size($memory_limit);
if (!drupal_check_memory_limit($usage + $required, $memory_limit)) {
$size = Bytes::toInt($memory_limit);
if (!Environment::checkMemoryLimit($usage + $required, $memory_limit)) {
drupal_set_message(t('There is not enough memory available to PHP to change this theme\'s color scheme. You need at least %size more. Check the <a href="@url">PHP documentation</a> for more information.', array('%size' => format_size($usage + $required - $size), '@url' => 'http://www.php.net/manual/ini.core.php#ini.sect.resource-limits')), 'error');
return;
}

View File

@ -7,6 +7,7 @@
namespace Drupal\editor\Form;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\Form\FormBase;
use Drupal\filter\Entity\FilterFormat;
use Drupal\Core\Ajax\AjaxResponse;
@ -55,7 +56,7 @@ class EditorImageDialog extends FormBase {
else {
$max_dimensions = 0;
}
$max_filesize = min(parse_size($image_upload['max_size']), file_upload_max_size());
$max_filesize = min(Bytes::toInt($image_upload['max_size']), file_upload_max_size());
$existing_file = isset($image_element['data-editor-file-uuid']) ? entity_load_by_uuid('file', $image_element['data-editor-file-uuid']) : NULL;
$fid = $existing_file ? $existing_file->id() : NULL;

View File

@ -7,6 +7,7 @@
namespace Drupal\file\Plugin\Field\FieldType;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\TypedData\DataDefinition;
@ -240,13 +241,13 @@ class FileItem extends EntityReferenceItem {
* Form API callback.
*
* Ensures that a size has been entered and that it can be parsed by
* parse_size().
* \Drupal\Component\Utility\Bytes::toInt().
*
* This function is assigned as an #element_validate callback in
* instanceSettingsForm().
*/
public static function validateMaxFilesize($element, &$form_state) {
if (!empty($element['#value']) && !is_numeric(parse_size($element['#value']))) {
if (!empty($element['#value']) && !is_numeric(Bytes::toInt($element['#value']))) {
form_error($element, $form_state, t('The "!name" option must contain a valid value. You may either leave the text field empty or enter a string like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes).', array('!name' => t($element['title']))));
}
}
@ -284,9 +285,9 @@ class FileItem extends EntityReferenceItem {
$settings = $this->getSettings();
// Cap the upload size according to the PHP limit.
$max_filesize = parse_size(file_upload_max_size());
$max_filesize = Bytes::toInt(file_upload_max_size());
if (!empty($settings['max_filesize'])) {
$max_filesize = min($max_filesize, parse_size($settings['max_filesize']));
$max_filesize = min($max_filesize, Bytes::toInt($settings['max_filesize']));
}
// There is always a file size limit due to the PHP server limit.

View File

@ -5,6 +5,8 @@
* Install, update and uninstall functions for the simpletest module.
*/
use Drupal\Component\Utility\Environment;
/**
* Minimum value of PHP memory_limit for SimpleTest.
*/
@ -53,7 +55,7 @@ function simpletest_requirements($phase) {
// Check the current memory limit. If it is set too low, SimpleTest will fail
// to load all tests and throw a fatal error.
$memory_limit = ini_get('memory_limit');
if (!drupal_check_memory_limit(SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
if (!Environment::checkMemoryLimit(SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
$requirements['php_memory_limit']['severity'] = REQUIREMENT_ERROR;
$requirements['php_memory_limit']['description'] = t('The testing framework requires the PHP memory limit to be at least %memory_minimum_limit. The current value is %memory_limit. <a href="@url">Follow these steps to continue</a>.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, '@url' => 'http://drupal.org/node/207036'));
}

View File

@ -1,46 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\system\Tests\Bootstrap\MiscUnitTest.
*/
namespace Drupal\system\Tests\Bootstrap;
use Drupal\simpletest\UnitTestBase;
/**
* Tests miscellaneous functions in bootstrap.inc.
*/
class MiscUnitTest extends UnitTestBase {
public static function getInfo() {
return array(
'name' => 'Miscellaneous bootstrap unit tests',
'description' => 'Test miscellaneous functions in bootstrap.inc.',
'group' => 'Bootstrap',
);
}
/**
* Tests that the drupal_check_memory_limit() function works as expected.
*/
function testCheckMemoryLimit() {
$memory_limit = ini_get('memory_limit');
// Test that a very reasonable amount of memory is available.
$this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
// Get the available memory and multiply it by two to make it unreasonably
// high.
$twice_avail_memory = ($memory_limit * 2) . 'MB';
// The function should always return true if the memory limit is set to -1.
$this->assertTrue(drupal_check_memory_limit($twice_avail_memory, -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
// Test that even though we have 30MB of memory available - the function
// returns FALSE when given an upper limit for how much memory can be used.
$this->assertFalse(drupal_check_memory_limit('30MB', '16MB'), 'drupal_check_memory_limit() returns FALSE with a 16MB upper limit on a 30MB requirement.');
// Test that an equal amount of memory to the amount requested returns TRUE.
$this->assertTrue(drupal_check_memory_limit('30MB', '30MB'), 'drupal_check_memory_limit() returns TRUE when requesting 30MB on a 30MB requirement.');
}
}

View File

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\Bytes;
use Drupal\simpletest\UnitTestBase;
/**
@ -25,7 +26,7 @@ class SizeUnitTest extends UnitTestBase {
}
function setUp() {
$kb = DRUPAL_KILOBYTE;
$kb = Bytes::KILOBYTE;
$this->exact_test_cases = array(
'1 byte' => 1,
'1 KB' => $kb,
@ -63,46 +64,13 @@ class SizeUnitTest extends UnitTestBase {
}
/**
* Checks that parse_size() returns the proper byte sizes.
*/
function testCommonParseSize() {
foreach ($this->exact_test_cases as $string => $size) {
$this->assertEqual(
$parsed_size = parse_size($string),
$size,
$size . ' == ' . $parsed_size . ' (' . $string . ')'
);
}
// Some custom parsing tests
$string = '23476892 bytes';
$this->assertEqual(
($parsed_size = parse_size($string)),
$size = 23476892,
$string . ' == ' . $parsed_size . ' bytes'
);
$string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB
$this->assertEqual(
$parsed_size = parse_size($string),
$size = 79691776,
$string . ' == ' . $parsed_size . ' bytes'
);
$string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB
$this->assertEqual(
$parsed_size = parse_size($string),
$size = 81862076662,
$string . ' == ' . $parsed_size . ' bytes'
);
}
/**
* Cross-tests parse_size() and format_size().
* Cross-tests Bytes::toInt() and format_size().
*/
function testCommonParseSizeFormatSize() {
foreach ($this->exact_test_cases as $size) {
$this->assertEqual(
$size,
($parsed_size = parse_size($string = format_size($size, NULL))),
($parsed_size = Bytes::toInt($string = format_size($size, NULL))),
$size . ' == ' . $parsed_size . ' (' . $string . ')'
);
}

View File

@ -6,6 +6,7 @@
*/
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Environment;
use Drupal\Core\Database\Database;
use Drupal\Core\Language\Language;
use Drupal\Core\Site\Settings;
@ -183,7 +184,7 @@ function system_requirements($phase) {
'value' => $memory_limit == -1 ? t('-1 (Unlimited)') : $memory_limit,
);
if (!drupal_check_memory_limit(DRUPAL_MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
if (!Environment::checkMemoryLimit(DRUPAL_MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
$description = '';
if ($phase == 'install') {
$description = t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the installation process.', array('%memory_minimum_limit' => DRUPAL_MINIMUM_PHP_MEMORY_LIMIT));

View File

@ -0,0 +1,75 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\BytesTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Bytes;
use Drupal\Tests\UnitTestCase;
/**
* Tests bytes size parsing helper methods.
*
* @group Drupal
* @group Utility
* @coversDefaultClass \Drupal\Component\Utility\Bytes
*/
class BytesTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Bytes utility helpers',
'description' => '',
'group' => 'Utility',
);
}
/**
* Tests \Drupal\Component\Utility\Bytes::toInt().
*
* @param int $size
* The value for the size argument for
* \Drupal\Component\Utility\Bytes::toInt().
* @param int $expected_int
* The expected return value from
* \Drupal\Component\Utility\Bytes::toInt().
*
* @dataProvider providerTestToInt
* @covers ::toInt
*/
public function testToInt($size, $expected_int) {
$this->assertEquals($expected_int, Bytes::toInt($size));
}
/**
* Provides data for testToInt.
*
* @return array
* An array of arrays, each containing the argument for
* \Drupal\Component\Utility\Bytes::toInt(): size, and the expected return
* value.
*/
public function providerTestToInt() {
return array(
array('1 byte', 1),
array('1 KB' , Bytes::KILOBYTE),
array('1 MB' , pow(Bytes::KILOBYTE, 2)),
array('1 GB' , pow(Bytes::KILOBYTE, 3)),
array('1 TB' , pow(Bytes::KILOBYTE, 4)),
array('1 PB' , pow(Bytes::KILOBYTE, 5)),
array('1 EB' , pow(Bytes::KILOBYTE, 6)),
array('1 ZB' , pow(Bytes::KILOBYTE, 7)),
array('1 YB' , pow(Bytes::KILOBYTE, 8)),
array('23476892 bytes', 23476892),
array('76MRandomStringThatShouldBeIgnoredByParseSize.', 79691776), // 76 MB
array('76.24 Giggabyte', 81862076662), // 76.24 GB (with typo)
);
}
}

View File

@ -0,0 +1,78 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\EnvironmentTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Environment;
use Drupal\Tests\UnitTestCase;
/**
* Tests PHP environment helper class.
*
* @group Drupal
* @group Utility
* @coversDefaultClass \Drupal\Component\Utility\Environment
*/
class EnvironmentTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'PHP environment utility helpers',
'description' => '',
'group' => 'Utility',
);
}
/**
* Tests \Drupal\Component\Utility\Environment::checkMemoryLimit().
*
* @param string $required
* The required memory argument for
* \Drupal\Component\Utility\Environment::checkMemoryLimit().
* @param string $custom_memory_limit
* The custom memory limit argument for
* \Drupal\Component\Utility\Environment::checkMemoryLimit().
* @param bool $expected
* The expected return value from
* \Drupal\Component\Utility\Environment::checkMemoryLimit().
*
* @dataProvider providerTestCheckMemoryLimit
* @covers ::checkMemoryLimit
*/
public function testCheckMemoryLimit($required, $custom_memory_limit, $expected) {
$actual = Environment::checkMemoryLimit($required, $custom_memory_limit);
$this->assertEquals($expected, $actual);
}
/**
* Provides data for testCheckMemoryLimit().
*
* @return array
* An array of arrays, each containing the arguments for
* \Drupal\Component\Utility\Environment::checkMemoryLimit():
* required and memory_limit, and the expected return value.
*/
public function providerTestCheckMemoryLimit() {
$memory_limit = ini_get('memory_limit');
$twice_avail_memory = ($memory_limit * 2) . 'MB';
return array(
// Minimal amount of memory should be available.
array('30MB', NULL, TRUE),
// Exceed a custom (unlimited) memory limit.
array($twice_avail_memory, -1, TRUE),
// Exceed a custom memory limit.
array('30MB', '16MB', FALSE),
// Available = required.
array('30MB', '30MB', TRUE),
);
}
}