1

I have this class which Resizes a given image to the set size and it can also put a watermark(stamp) on it.

  class Picture
    {

    private $src_path, $src_image, $width, $height, $resized_image;

    public function __construct($src_path)
    {
        if (!file_exists($src_path)) {
            throw new InvalidArgumentException('Invalid image path!');
        }
        $this->src_path = $src_path;

        $this->loadImage();
    }

    private function loadImage()
    {
        if (exif_imagetype($this->src_path) == IMAGETYPE_JPEG) {
            $this->src_image = imagecreatefromjpeg($this->src_path);
        } elseif (exif_imagetype($this->src_path) == IMAGETYPE_PNG) {
            $this->src_image = imagecreatefrompng($this->src_path);
        } else {
            throw new InvalidArgumentException('Your image type is not JPEG or PNG!');
        }
    }

    public function waterMark()
    {
        $stamp = imagecreatefromjpeg('C:\Users\Public\Pictures\Sample Pictures\resized.jpg');
        $im = $this->resized_image;

        $marge_right = 10;
        $marge_bottom = 80;
        $sx = imagesx($stamp);
        $sy = imagesy($stamp);

        // Copy the stamp image onto our photo using the margin offsets and the photo 
        // width to calculate positioning of the stamp. 
        imagecopy($im, $stamp, imagesx($im) - $sx - $marge_right, imagesy($im) - $sy - $marge_bottom, 0, 0, imagesx($stamp), imagesy($stamp));
    }

    public function resizeImage($w, $h)
    {
        list($width, $height) = getimagesize($this->src_path);
        $ratio = $width / $height;

        if ($w / $h > $ratio) {
            $new_width = $h * $ratio;
            $new_height = $h;
        }else {
            $new_height = $w / $ratio;
            $new_width = $w;
        }

        $this->resized_image = imagecreatetruecolor($new_width, $new_height);

        imagecopyresampled($this->resized_image, $this->src_image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
    }

    public function saveImage($quality, $destination = null)
    {
        if (!$destination) {
            $length = strlen($this->src_path) - strlen((string) $this->src_image);

            $destination = substr($this->src_path, 0, $length-3).'resized.jpg';
        }

        return imagejpeg($this->resized_image, $destination, $quality);
    }
}

$pic = new Picture('C:/Users/Public/Pictures/Sample Pictures/Chrysanthemum.jpg');

$pic->resizeImage(400, 400);
$pic->saveImage(100, 'C:/Users/Public/Pictures/ResizedImage.jpg');
$pic->waterMark();

//Why is the original picture ($this->resized_image)
//is now watermarked ?
//Why is there a reference here ?
$pic->saveImage(100, 'C:/Users/Public/Pictures/WhyAmIWaterMarked.jpg');

My question is after I use the WaterMark() function why is the $this->resized_image variable changes to the value of the WaterMarked one ?

$im = $this->resized_image;
imagecopy($im, $stamp, imagesx($im) - $sx - $marge_right, imagesy($im) - $sy - $marge_bottom, 0, 0, imagesx($stamp), imagesy($stamp));

The result:

I wrote another class just to test this behaviour:

class TestReference
{
    public $foo;

    function setFoo($value)
    {
        $this->foo = $value;
    }

    function getFoo()
    {
        return $this->foo;
    }

    function getFooBar()
    {
        $tmp = $this->foo;
        $tmp = 20;

        return $this->foo;
    }
}

$bar = new TestReference();
$bar->setFoo(15);
echo $bar->getFoo().'<br/>';
echo $bar->getFooBar().'<br/>';
echo $bar->getFoo().'<br/>';

But the result is as expected: 15, 15, 15;

So how come in the Picture class that variable($this->resized_image) is overrwriten by a temporary variable ?

tereško
  • 58,060
  • 25
  • 98
  • 150
Beni
  • 149
  • 2
  • 13
  • Your test class works as expected. `$tmp` is 20, `$this->foo` is unchanged. – FirstOne Nov 21 '17 at 12:14
  • I couldn't find where you set the first value of `resized_image`, but if it's an object, objects are basically references. – FirstOne Nov 21 '17 at 12:14
  • 2
    The `resource` type (as returned by `imagecreatefromjpeg()`) is effectively passed by reference - it has the same semantics as an object. If you want to create a copy of an image resource, you need to create a new resource. See https://stackoverflow.com/questions/12605768/how-to-clone-a-gd-resource-in-php for more information on how to do this. – DaveRandom Nov 21 '17 at 12:16
  • 2
    The question seems clear to me, are people lazy to read it? I know I am xD but I guess people are free to vote as they please.. – FirstOne Nov 21 '17 at 12:17
  • 1
    Because `$this->resized_image` is just holding a reference to a [resource](http://php.net/manual/en/language.types.resource.php)? So you are actually passing the same reference to another variable thus creating another link to the same resource. – DarkBee Nov 21 '17 at 12:20
  • @DaveRandom If it has the same semantics as an object, this should work as expected. Objects aren't passed as references **like that** – Xatenev Nov 21 '17 at 12:20
  • @DarkBee See what I wrote above. – Xatenev Nov 21 '17 at 12:22
  • 2
    @Xatenev The result of the code is the expected behaviour for the by-ref semantics of `resource`. 1) Constructor opens source image. 2) `resizeImage()` sets `$this->resized_image` to resized copy of the source image. 3) `saveImage()` outputs `$this->resized_image` to a file. 4) `waterMark()` adds a watermark **to `$this->resized_image`**, because `$im = $this->resized_image` **does not create a copy**. 5) `saveImage()` outputs `$this->resized_image` (now watermarked) to a file. – DaveRandom Nov 21 '17 at 12:48
  • @Xatenev [demo](https://ideone.com/afC55l) with regular objects – DarkBee Nov 21 '17 at 13:09

0 Answers0