Zulakis
Zulakis

Reputation: 8374

PHP get actual maximum upload size

When using

ini_get("upload_max_filesize");

it actually gives you the string specified in the php.ini file.

It is not good to use this value as a reference for the maximum upload size because

So, is there any way to get the value actually being used by PHP, besides the one reported by ini_get, or what is the best way to determinate it?

Upvotes: 70

Views: 134020

Answers (6)

Deckard
Deckard

Reputation: 2450

Here is the full solution. It takes care of all traps like the shorthand byte notation and also considers post_max_size:

/**
* This function returns the maximum files size that can be uploaded 
* in PHP
* @returns int File size in bytes
**/
function getMaximumFileUploadSize()  
{  
    return min(convertPHPSizeToBytes(ini_get('post_max_size')), convertPHPSizeToBytes(ini_get('upload_max_filesize')));  
}  

/**
* This function transforms the php.ini notation for numbers (like '2M') to an integer (2*1024*1024 in this case)
* 
* @param string $sSize
* @return integer The value in bytes
*/
function convertPHPSizeToBytes($sSize)
{
    //
    $sSuffix = strtoupper(substr($sSize, -1));
    if (!in_array($sSuffix,array('P','T','G','M','K'))){
        return (int)$sSize;  
    } 
    $iValue = substr($sSize, 0, -1);
    switch ($sSuffix) {
        case 'P':
            $iValue *= 1024;
            // Fallthrough intended
        case 'T':
            $iValue *= 1024;
            // Fallthrough intended
        case 'G':
            $iValue *= 1024;
            // Fallthrough intended
        case 'M':
            $iValue *= 1024;
            // Fallthrough intended
        case 'K':
            $iValue *= 1024;
            break;
    }
    return (int)$iValue;
}      

Upvotes: 46

meustrus
meustrus

Reputation: 7315

Drupal has this implemented fairly elegantly:

// Returns a file size limit in bytes based on the PHP upload_max_filesize
// and post_max_size
function file_upload_max_size() {
  static $max_size = -1;

  if ($max_size < 0) {
    // Start with post_max_size.
    $post_max_size = parse_size(ini_get('post_max_size'));
    if ($post_max_size > 0) {
      $max_size = $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'));
    if ($upload_max > 0 && $upload_max < $max_size) {
      $max_size = $upload_max;
    }
  }
  return $max_size;
}

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(1024, stripos('bkmgtpezy', $unit[0])));
  }
  else {
    return round($size);
  }
}

The above functions are available anywhere in Drupal, or you can copy it and use it in your own project subject to the terms of the GPL license version 2 or later.

As for parts 2 and 3 of your question, you will need to parse the php.ini file directly. These are essentially configuration errors, and PHP is resorting to fallback behaviors. It appears you can actually get the location of the loaded php.ini file in PHP, although trying to read from it may not work with basedir or safe-mode enabled:

$max_size = -1;
$post_overhead = 1024; // POST data contains more than just the file upload; see comment from @jlh
$files = array_merge(array(php_ini_loaded_file()), explode(",\n", php_ini_scanned_files()));
foreach (array_filter($files) as $file) {
  $ini = parse_ini_file($file);
  $regex = '/^([0-9]+)([bkmgtpezy])$/i';
  if (!empty($ini['post_max_size']) && preg_match($regex, $ini['post_max_size'], $match)) {
    $post_max_size = round($match[1] * pow(1024, stripos('bkmgtpezy', strtolower($match[2])));
    if ($post_max_size > 0) {
      $max_size = $post_max_size - $post_overhead;
    }
  }
  if (!empty($ini['upload_max_filesize']) && preg_match($regex, $ini['upload_max_filesize'], $match)) {
    $upload_max_filesize = round($match[1] * pow(1024, stripos('bkmgtpezy', strtolower($match[2])));
    if ($upload_max_filesize > 0 && ($max_size <= 0 || $max_size > $upload_max_filesize) {
      $max_size = $upload_max_filesize;
    }
  }
}

echo $max_size;

Upvotes: 81

jcampbell1
jcampbell1

Reputation: 4529

This is what I use:

function asBytes($ini_v) {
   $ini_v = trim($ini_v);
   $s = [ 'g'=> 1<<30, 'm' => 1<<20, 'k' => 1<<10 ];
   return intval($ini_v) * ($s[strtolower(substr($ini_v,-1))] ?: 1);
}

Upvotes: 11

Zulakis
Zulakis

Reputation: 8374

Looks like it isn't possible.

Because of this, I am going to continue using this code:

function convertBytes( $value ) {
    if ( is_numeric( $value ) ) {
        return $value;
    } else {
        $value_length = strlen($value);
        $qty = substr( $value, 0, $value_length - 1 );
        $unit = strtolower( substr( $value, $value_length - 1 ) );
        switch ( $unit ) {
            case 'k':
                $qty *= 1024;
                break;
            case 'm':
                $qty *= 1048576;
                break;
            case 'g':
                $qty *= 1073741824;
                break;
        }
        return $qty;
    }
}
$maxFileSize = convertBytes(ini_get('upload_max_filesize'));

Originally from this helpful php.net comment.

STILL OPEN TO ACCEPT BETTER ANSWERS

Upvotes: 5

Martin
Martin

Reputation: 362

Well you can always use this syntax, which will give you correct numbers from PHP ini file:

$maxUpload      = (int)(ini_get('upload_max_filesize'));
$maxPost        = (int)(ini_get('post_max_size'));

Mart

Upvotes: -1

Thomas
Thomas

Reputation: 1432

I don't think so, at least not in the way you have defined it. There are so many other factors that come into consideration for maximum file upload size, most notably the connection speed of the user as well as the timeout setting for the web server as well as the PHP process(es).

A more useful metric for you might be to decide what is a reasonable maximum file size for the types of files you expect to receive for a given input. Make the decision on what is reasonable for your use case and set a policy around that.

Upvotes: 0

Related Questions