/** * Validates a $_FILES array against the upload configuration * * @param array $file_array The $_FILES array for a single file * @return string The validation error message */ private function validateField($file_array) { if (empty($file_array['name'])) { if ($this->required) { return self::compose('Please upload a file'); } return NULL; } if ($file_array['error'] == UPLOAD_ERR_FORM_SIZE || $file_array['error'] == UPLOAD_ERR_INI_SIZE) { $max_size = !empty($_POST['MAX_FILE_SIZE']) ? $_POST['MAX_FILE_SIZE'] : ini_get('upload_max_filesize'); $max_size = !is_numeric($max_size) ? fFilesystem::convertToBytes($max_size) : $max_size; return self::compose('The file uploaded is over the limit of %s', fFilesystem::formatFilesize($max_size)); } if ($this->max_size && $file_array['size'] > $this->max_size) { return self::compose('The file uploaded is over the limit of %s', fFilesystem::formatFilesize($this->max_size)); } if (empty($file_array['tmp_name']) || empty($file_array['size'])) { if ($this->required) { return self::compose('Please upload a file'); } return NULL; } if (!empty($this->mime_types) && file_exists($file_array['tmp_name'])) { $contents = file_get_contents($file_array['tmp_name'], FALSE, NULL, 0, 4096); if (!in_array(fFile::determineMimeType($file_array['name'], $contents), $this->mime_types)) { return self::compose($this->mime_type_message); } } if (!$this->allow_php) { $file_info = fFilesystem::getPathInfo($file_array['name']); if (in_array(strtolower($file_info['extension']), array('php', 'php4', 'php5'))) { return self::compose('The file uploaded is a PHP file, but those are not permitted'); } } if (!$this->allow_dot_files) { if (substr($file_array['name'], 0, 1) == '.') { return self::compose('The name of the uploaded file may not being with a .'); } } if ($this->image_dimensions && file_exists($file_array['tmp_name'])) { if (fImage::isImageCompatible($file_array['tmp_name'])) { list($width, $height, $other) = getimagesize($file_array['tmp_name']); if ($this->image_dimensions['min_width'] && $width < $this->image_dimensions['min_width']) { return self::compose('The uploaded image is narrower than the minimum width of %spx', $this->image_dimensions['min_width']); } if ($this->image_dimensions['min_height'] && $height < $this->image_dimensions['min_height']) { return self::compose('The uploaded image is shorter than the minimum height of %spx', $this->image_dimensions['min_height']); } if ($this->image_dimensions['max_width'] && $width > $this->image_dimensions['max_width']) { return self::compose('The uploaded image is wider than the maximum width of %spx', $this->image_dimensions['max_width']); } if ($this->image_dimensions['max_height'] && $height > $this->image_dimensions['max_height']) { return self::compose('The uploaded image is taller than the maximum height of %spx', $this->image_dimensions['max_height']); } } } if ($this->image_ratio && file_exists($file_array['tmp_name'])) { if (fImage::isImageCompatible($file_array['tmp_name'])) { list($width, $height, $other) = getimagesize($file_array['tmp_name']); if ($this->image_ratio['allow_excess_dimension'] == 'width' && $width / $height < $this->image_ratio['width'] / $this->image_ratio['height']) { return self::compose('The uploaded image is too narrow for its height. The required ratio is %1$sx%2$s or wider.', $this->image_ratio['width'], $this->image_ratio['height']); } if ($this->image_ratio['allow_excess_dimension'] == 'height' && $width / $height > $this->image_ratio['width'] / $this->image_ratio['height']) { return self::compose('The uploaded image is too short for its width. The required ratio is %1$sx%2$s or taller.', $this->image_ratio['width'], $this->image_ratio['height']); } } } }
/** * @dataProvider convertToBytesProvider */ public function testConvertToBytes($input, $output) { $this->assertEquals($output, fFilesystem::convertToBytes($input)); }
/** * Validates a $_FILES array against the upload configuration * * @param array $file_array The $_FILES array for a single file * @return string The validation error message */ private function validateField($file_array) { if (empty($file_array['name'])) { if ($this->required) { return self::compose('Please upload a file'); } return NULL; } if ($file_array['error'] == UPLOAD_ERR_FORM_SIZE || $file_array['error'] == UPLOAD_ERR_INI_SIZE) { $max_size = !empty($_POST['MAX_FILE_SIZE']) ? $_POST['MAX_FILE_SIZE'] : ini_get('upload_max_filesize'); $max_size = !is_numeric($max_size) ? fFilesystem::convertToBytes($max_size) : $max_size; return self::compose('The file uploaded is over the limit of %s', fFilesystem::formatFilesize($max_size)); } if ($this->max_size && $file_array['size'] > $this->max_size) { return self::compose('The file uploaded is over the limit of %s', fFilesystem::formatFilesize($this->max_size)); } if (empty($file_array['tmp_name']) || empty($file_array['size'])) { if ($this->required) { return self::compose('Please upload a file'); } return NULL; } if (!empty($this->mime_types) && file_exists($file_array['tmp_name'])) { $contents = file_get_contents($file_array['tmp_name'], FALSE, NULL, 0, 4096); if (!in_array(fFile::determineMimeType($file_array['name'], $contents), $this->mime_types)) { return self::compose($this->mime_type_message); } } if (!$this->allow_php) { $file_info = fFilesystem::getPathInfo($file_array['name']); if (in_array(strtolower($file_info['extension']), array('php', 'php4', 'php5'))) { return self::compose('The file uploaded is a PHP file, but those are not permitted'); } } if (!$this->allow_dot_files) { if (substr($file_array['name'], 0, 1) == '.') { return self::compose('The name of the uploaded file may not being with a .'); } } }
/** * Validates the uploaded file, ensuring a file was actually uploaded and that is matched the restrictions put in place * * @throws fValidationException When no file is uploaded or the uploaded file violates the options set for this object * * @param string $field The field the file was uploaded through * @param integer $index If the field was an array of file uploads, this specifies which one to validate * @return void */ public function validate($field, $index = NULL) { if (!self::check($field)) { throw new fProgrammerException('The field specified, %s, does not appear to be a file upload field', $field); } $file_array = $this->extractFileUploadArray($field, $index); // Do some validation of the file provided if (empty($file_array['name'])) { throw new fValidationException('Please upload a file'); } if ($file_array['error'] == UPLOAD_ERR_FORM_SIZE || $file_array['error'] == UPLOAD_ERR_INI_SIZE) { $max_size = !empty($_POST['MAX_FILE_SIZE']) ? $_POST['MAX_FILE_SIZE'] : ini_get('upload_max_filesize'); $max_size = !is_numeric($max_size) ? fFilesystem::convertToBytes($max_size) : $max_size; $msg = $this->max_message != "" ? $this->max_message : 'The file uploaded is over the limit of %s'; throw new fValidationException($msg, fFilesystem::formatFilesize($max_size)); } if ($this->max_file_size && $file_array['size'] > $this->max_file_size) { $msg = $this->max_message != "" ? $this->max_message : 'The file uploaded is over the limit of %s'; throw new fValidationException($msg, fFilesystem::formatFilesize($this->max_file_size)); } if (empty($file_array['tmp_name']) || empty($file_array['size'])) { throw new fValidationException('Please upload a file'); } if (!empty($this->mime_types) && file_exists($file_array['tmp_name']) && !in_array(fFile::determineMimeType($file_array['tmp_name']), $this->mime_types)) { throw new fValidationException($this->mime_type_message); } if (!$this->allow_php) { $file_info = fFilesystem::getPathInfo($file_array['name']); if (in_array(strtolower($file_info['extension']), array('php', 'php4', 'php5'))) { throw new fValidationException('The file uploaded is a PHP file, but those are not permitted'); } } return $file_array; }
/** * Processes the current image using GD * * @param string $output_file The file to save the image to * @param integer $jpeg_quality The JPEG quality to use * @return void */ private function processWithGD($output_file, $jpeg_quality) { $type = self::getImageType($this->file); $save_alpha = FALSE; $path_info = fFilesystem::getPathInfo($output_file); $new_type = $path_info['extension']; $new_type = $type == 'jpeg' ? 'jpg' : $type; if (!in_array($new_type, array('gif', 'jpg', 'png'))) { $new_type = $type; } if (ini_get('memory_limit') != '-1') { // We will estimate memory usage at 3MB if we can't actually check it $beginning_memory_usage = 3145728; if (function_exists('memory_get_usage')) { $beginning_memory_usage = memory_get_usage(); } $memory_limit_bytes = fFilesystem::convertToBytes(ini_get('memory_limit')); // Estimate the memory usage and throw an exception if we will run out $load_byte_usage = $this->pending_modifications[0]['old_width'] * $this->pending_modifications[0]['old_height'] * 4; if ($load_byte_usage + $beginning_memory_usage > $memory_limit_bytes) { throw new fEnvironmentException('The predicted memory usage to complete the image modifications using the GD extension, %1$s, will most likely exceed the memory limit of %2$s', $load_byte_usage + $beginning_memory_usage, $memory_limit_bytes); } } switch ($type) { case 'gif': $gd_res = imagecreatefromgif($this->file); $save_alpha = TRUE; break; case 'jpg': $gd_res = imagecreatefromjpeg($this->file); break; case 'png': $gd_res = imagecreatefrompng($this->file); $save_alpha = TRUE; break; } foreach ($this->pending_modifications as $num => $mod) { if (ini_get('memory_limit') != '-1') { $old_byte_usage = $this->pending_modifications[0]['old_width'] * $this->pending_modifications[0]['old_height'] * 4; $new_byte_usage = $this->pending_modifications[0]['width'] * $this->pending_modifications[0]['height'] * 4; if ($old_byte_usage + $new_byte_usage + $beginning_memory_usage > $memory_limit_bytes) { throw new fEnvironmentException('The predicted memory usage to complete the image modifications using the GD extension, %1$s, will most likely exceed the memory limit of %2$s', $old_byte_usage + $new_byte_usage + $beginning_memory_usage, $memory_limit_bytes); } } $new_gd_res = imagecreatetruecolor($mod['width'], $mod['height']); if ($save_alpha) { imagealphablending($new_gd_res, FALSE); imagesavealpha($new_gd_res, TRUE); if ($new_type == 'gif') { $transparent = imagecolorallocatealpha($new_gd_res, 255, 255, 255, 127); imagefilledrectangle($new_gd_res, 0, 0, $mod['width'], $mod['height'], $transparent); imagecolortransparent($new_gd_res, $transparent); } } // Perform the resize operation if ($mod['operation'] == 'resize') { imagecopyresampled($new_gd_res, $gd_res, 0, 0, 0, 0, $mod['width'], $mod['height'], $mod['old_width'], $mod['old_height']); // Perform the crop operation } elseif ($mod['operation'] == 'crop') { imagecopyresampled($new_gd_res, $gd_res, 0, 0, $mod['start_x'], $mod['start_y'], $mod['width'], $mod['height'], $mod['width'], $mod['height']); // Perform the desaturate operation } elseif ($mod['operation'] == 'desaturate') { // Create a palette of grays $grays = array(); for ($i = 0; $i < 256; $i++) { $grays[$i] = imagecolorallocate($new_gd_res, $i, $i, $i); } $transparent = imagecolorallocatealpha($new_gd_res, 255, 255, 255, 127); // Loop through every pixel and convert the rgb values to grays for ($x = 0; $x < $mod['width']; $x++) { for ($y = 0; $y < $mod['height']; $y++) { $color = imagecolorat($gd_res, $x, $y); if ($type != 'gif') { $red = $color >> 16 & 0xff; $green = $color >> 8 & 0xff; $blue = $color & 0xff; if ($save_alpha) { $alpha = $color >> 24 & 0x7f; } } else { $color_info = imagecolorsforindex($gd_res, $color); $red = $color_info['red']; $green = $color_info['green']; $blue = $color_info['blue']; $alpha = $color_info['alpha']; } if (!$save_alpha || $alpha != 127) { // Get the appropriate gray (http://en.wikipedia.org/wiki/YIQ) $yiq = round($red * 0.299 + $green * 0.587 + $blue * 0.114); if (!$save_alpha || $alpha == 0) { $new_color = $grays[$yiq]; } else { $new_color = imagecolorallocatealpha($new_gd_res, $yiq, $yiq, $yiq, $alpha); } } else { $new_color = $transparent; } imagesetpixel($new_gd_res, $x, $y, $new_color); } } // Perform the rotate operation } elseif ($mod['operation'] == 'rotate') { // The imagerotate() function is only available if the PHP-bundled // version of GD is used, which is not always the case (e.g. debian/ubuntu) if (function_exists('imagerotate')) { // For some reason imagerotate() seem to rotate counter-clockwise if ($mod['degrees'] == 90) { $mod['degrees'] = 270; } elseif ($mod['degrees'] == 270) { $mod['degrees'] = 90; } // If the source image is not true color, we need to convert // to a true color image first, otherwise imagerotate() fails // and returns false, causing no image to be saved if (imagecolorstotal($gd_res)) { imagecopy($new_gd_res, $gd_res, 0, 0, 0, 0, $mod['width'], $mod['height']); imagedestroy($gd_res); $gd_res = $new_gd_res; unset($new_gd_res); } $new_gd_res = imagerotate($gd_res, $mod['degrees'], -1); // If you don't set the alpha mode for PNG, images that // contain transparency and are rotated will be distored // in odd ways if ($new_type == 'png') { imagealphablending($new_gd_res, false); imagesavealpha($new_gd_res, true); } } else { switch ($mod['degrees']) { case 90: for ($x = 0; $x < $mod['width']; $x++) { for ($y = 0; $y < $mod['height']; $y++) { imagecopy($new_gd_res, $gd_res, $mod['height'] - $y - 1, $x, $x, $y, 1, 1); } } break; case 180: // Rather than copying one pixel at a time, like with 90 // and 270 degrees, for 180 degrees we can copy one rpw // at a time for better performance for ($x = 0; $x < $mod['width']; $x++) { imagecopy($new_gd_res, $gd_res, $mod['width'] - $x - 1, 0, $x, 0, 1, $mod['height']); } $row = imagecreatetruecolor($mod['width'], 1); for ($y = 0; $y < $mod['height'] / 2; $y++) { imagecopy($row, $new_gd_res, 0, 0, 0, $mod['height'] - $y - 1, $mod['width'], 1); imagecopy($new_gd_res, $new_gd_res, 0, $mod['height'] - $y - 1, 0, $y, $mod['width'], 1); imagecopy($new_gd_res, $row, 0, $y, 0, 0, $mod['width'], 1); } imagedestroy($row); break; case 270: for ($x = 0; $x < $mod['width']; $x++) { for ($y = 0; $y < $mod['height']; $y++) { imagecopy($new_gd_res, $gd_res, $y, $mod['width'] - $x - 1, $x, $y, 1, 1); } } break; } } } imagedestroy($gd_res); $gd_res = $new_gd_res; } // Save the file switch ($new_type) { case 'gif': imagetruecolortopalette($gd_res, TRUE, 256); imagegif($gd_res, $output_file); break; case 'jpg': imagejpeg($gd_res, $output_file, $jpeg_quality); break; case 'png': imagepng($gd_res, $output_file); break; } imagedestroy($gd_res); }