Ми}{@лbI4

Блог хеллоуворлдщика

Ошибка при загрузке больших файлов превышающих upload_max_filesize в php.ini. Validate CSRF token

18.03.2014 yii, CSRF

Столкнулся с этой неизлечимой из корня бедой и я тоже. В сети много костылей по решению данной проблемы. Самый популярный костыль это

отключить проверку CSRF при определенном route

я не стал следовать этому совету и решил разобраться глубже. Первое что я сделал, это проверил что находится в момент ошибки в $_SERVER и сделал выводы, используя которые было написано моё решение.

class HttpRequest extends CHttpRequest
{
  
    /**
     * @var array - Media types and the sub-types, which content the field for upload files
     */
    public $contentType = array('multipart/form-data');
  
    public function validateCsrfToken($event)
    {
        $currentType = explode(';', $_SERVER['CONTENT_TYPE']);
        $contentLength = $_SERVER['CONTENT_LENGTH'];
  
        if ($this->getIsPostRequest() && in_array($currentType[0], $this->contentType) && $contentLength)
        {
            if ($contentLength > $this->sizeToBytes(ini_get('upload_max_filesize')))
            {
                Yii::app()->user->setFlash('error', 'The file is too large.');
                return true;
            }
        }
         
        parent::validateCsrfToken($event);
    }
  
    /**
     * Converts php.ini style size to bytes. Examples of size strings are: 150, 1g, 500k, 5M (size suffix
     * is case insensitive). If you pass here the number with a fractional part, then everything after
     * the decimal point will be ignored (php.ini values common behavior). For example 1.5G value would be
     * treated as 1G and 1073741824 number will be returned as a result. This method is public
     * (was private before) since 1.1.11.
     *
     * @param string $sizeStr the size string to convert.
     * @return integer the byte count in the given size string.
     * @since 1.1.11
     */
    public function sizeToBytes($sizeStr)
    {
        // get the latest character
        switch (strtolower(substr($sizeStr, -1)))
        {
            case 'm': return (int) $sizeStr * 1048576; // 1024 * 1024
            case 'k': return (int) $sizeStr * 1024; // 1024
            case 'g': return (int) $sizeStr * 1073741824; // 1024 * 1024 * 1024
            default: return (int) $sizeStr; // do nothing
        }
    }
   
}

Данный код сравнивает $_SERVER['CONTENT_LENGTH'] с ini_get('upload_max_filesize'). Если $_SERVER['CONTENT_LENGTH'] > ini_get('upload_max_filesize'), то выводится соответствующее сообщение. Его можно поменять, убрать или ещё что-нибудь. В свойстве $contentType класса HttpRequest содержится массив с media-типами, к которым следует применять данное решение.

Возможно, это не самое лучшее решение, но меня оно устраивает полностью.

Внимание! У некоторых могут возникнуть проблемы с конфликтом имён класса, т.к. класс HttpRequest есть в HTTP extension http://www.php.net/manual/en/class.httprequest.php . Чтобы решить данную проблему добавляем любой префикс в имя класса или используем namespace.