<?php
/**
 * Request like this: http://www.mysite.com/imgResizer.php?uri=http://www.mysite.com/images/1/1/0/8/3/6/6/0/blahimage.jpg&w=800
 *
 * Required parameters:
 * URI - The url of the image. Note that it must be on the same host (to avoid downloading external resources for performance reasons)
 *
 * Optional Parameters:
 * w - The desired width to scale to (if omitted then uses the real width)
 * h - The desired height to scale to (if omitted then uses the real height)
 * usepng - If 1 (defaults to 0), returns the output as a PNG instead of a JPEG
 * keepratio - If 1 (defaults to 1), preserves the aspect ratio when scaling.
 *
 * Class ImgResizer
 */
class ImgResizer {
    const WEBROOT = "/var/www";
    const DEFAULT_JPG_QUALITY = 85;
    const DEFAULT_PNG_COMPRESSION = 9;

    public function ImgResizer(){
        $url = $this->getUrlParam('uri', true);

        if($this->isValidUrl($url)){
            $imgUri = $this->buildImageUri($url);
            if(!file_exists($imgUri)){
                throw new \Exception('Requested image does not exist: ' . $imgUri);
            }
            $image = ImageHelper::resizeImage(ImageHelper::getImage($imgUri), $this->getUrlParam('w', false, 0),
                $this->getUrlParam('h', false, 0), $this->getUrlParam('keepratio', false, 1));
            if($this->getUrlParam('usepng', false, 0)){
                ImageHelper::showPNG($image, self::DEFAULT_PNG_COMPRESSION);
            } else {
                ImageHelper::showJPG($image, self::DEFAULT_JPG_QUALITY);
            }
        } else {
            throw new \InvalidArgumentException('External images not allowed');
        }
    }

    /**
     * Ensures that the requested image is on the same host as this script.
     *
     * @param string $url
     * @return bool
     */
    private function isValidUrl($url){
        $host = $_SERVER['HTTP_HOST'];
        $requestedHost = parse_url($url, PHP_URL_HOST);

        return ($requestedHost == $host or empty($requestedHost)); //also accept empty in case it's a relative path
    }

    /**
     * @param string $url
     * @return string
     * @throws InvalidArgumentException
     */
    private function buildImageUri($url){
        if(empty($url)) {
            throw new \InvalidArgumentException("Invalid (empty) image URI requested.");
        }

        $path = parse_url($url, PHP_URL_PATH);
        if($path[0] != '/') {
            $path = '/' . $path;
        }

        return self::WEBROOT . $path;
    }

    /**
     * @param string $param
     * @param bool $required
     * @param null $default
     * @return mixed|null
     * @throws InvalidArgumentException
     */
    private function getUrlParam($param, $required = false, $default = null){
        if(isset($_GET[$param])) {
            return $_GET[$param];
        }

        if($required) {
            throw new \InvalidArgumentException("Missing required argument: " . $param);
        }

        return $default;
    }
}

/**
 * Here are the various functions used to work with images, extracted to a separate class for better readability.
 *
 * Class ImageHelper
 */
class ImageHelper {
    /**
     * @param resource $image
     * @param int $width
     * @param int $height
     * @param bool $keepRatio
     * @return resource
     */
    public static function resizeImage($image, $width, $height, $keepRatio = true) {
        $imageX = imagesx($image);
        $imageY = imagesy($image);
        if(empty($width) or $imageX < $width){
            $width = $imageX;
        }
        if(empty($height) or $imageY < $height){
            $height = $imageY;
        }
        $imageAspectRatio = $imageX / $imageY;

        if($imageX < $imageY){
            $x = ($keepRatio) ? $imageAspectRatio * $height : $width;
            $y = $height;
        } else {
            $x = $width;
            $y = ($keepRatio) ? $width / $imageAspectRatio : $height;
        }

        $resizedImage = imagecreatetruecolor($x, $y);
        imagecopyresampled($resizedImage, $image, 0, 0, 0, 0, $x, $y, $imageX, $imageY);

        return $resizedImage;
    }

    /**
     * @param string $uri
     * @return resource
     * @throws InvalidArgumentException
     */
    public static function getImage($uri){
        @$image_info = getImageSize($uri);

        switch ($image_info['mime']) {
            case 'image/gif':
                if (imagetypes() & IMG_GIF) {
                    $image = imageCreateFromGIF($uri);
                }
                break;
            case 'image/jpeg':
                if (imagetypes() & IMG_JPG) {
                    $image = imageCreateFromJPEG($uri);
                }
                break;
            case 'image/png':
                if (imagetypes() & IMG_PNG) {
                    $image = imageCreateFromPNG($uri);
                }
                break;
            case 'image/wbmp':
                if (imagetypes() & IMG_WBMP) {
                    $image = imageCreateFromWBMP($uri);
                }
                break;
            default:
                throw new \InvalidArgumentException('Unknown mime type for image: ' . $uri);
        }
        @imagesavealpha($image, true);

        return $image;
    }


    /**
     * @param resource $image
     * @param int $quality
     */
    public static function showJPG($image, $quality){
        header('Content-Type: image/jpeg');
        echo imageJPEG($image, null, $quality);
    }

    /**
     * @param resource $image
     * @param int $compression
     */
    public static function showPNG($image, $compression){
        header('Content-Type: image/png');
        echo imagePNG($image, null, $compression);
    }
}

new ImgResizer(); //Here's the entry point