Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

Уроки PHP - урок 4 - Работа с изображениями, библиотека GD2.

08/12/2019, by Ivan

В прошлых уроках мы разобрались как писать запросы к БД, поэтому сейчас мы будем меньше внимания уделять как писать запросы, а будем просто тренероваться их писать. Будем также комбинировать написание запросов с изучением других возможностей PHP, начнем с обработки изображений. В одном из прошлых уроков мы уже загружали файлы, у нас даже есть таблица Files для загруженных файлов. Давайте загружать изображения в эту же таблицу. Но сначала нужно добавить поле для загрузки фотографии в форму создания материала.

$content .=	  '<label for="bodytext">Сообщение:</label><br />';
$content .=	  '<textarea name="bodytext" id="bodytext"></textarea><br />';
$content .=   '<label for="bodytext">Прикрепить файл:</label><br /><input type="file" name="filename"><br />'; // поле загрузки файла
$content .=   '<label for="bodytext">Прикрепить изображение:</label><br /><input type="file" name="image">'; // поле загрузки файла
	

Теперь нужно написать обработку файлов, но для начала добавим еще один столбец в таблице Messages, куда мы будем записывать fid изображений, а данные изображения мы будем записывать в таблицу Files, как и с остальными файлами. Колонку я назвал image_fid ее характеристики следующие INT(11) и NULL по умолчанию. Теперь давайте вставим обработку формы в метод write():

 if($_FILES["image"]["size"] > 1024*3*1024){
       echo ("Размер файла превышает три мегабайта");
       exit;
    }
   if(is_uploaded_file($_FILES["image"]["tmp_name"])){    // Проверяем загружен ли файл
     // Если файл загружен успешно, перемещаем его
     // из временной директории в конечную
     move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]);
   } else {
      echo("Ошибка загрузки файла");
   }	
   
	$sql = 'INSERT INTO Files (filename, filepath, filemime, filesize, timestamp) 
	VALUES ("'. $_FILES['image']['name'] . '", 
	 "files/' . $_FILES['image']['name'] . '",
	 "'. $_FILES['image']['type'] .'",
	 '. $_FILES['image']['size'] .',
	 '. time() . ')';  

	mysql_query($sql);  

Эта обработка не будет ничем отличаться от загрузки обычных файлов, хотя в одном из следующих уроков мы будем разбирать как сделать проверку какой тип файла загружается, чтобы под видом изображений не загружали скажем pdf-документы.

Обработку мы написали, теперь будут загружаться два файла, один из которых изображение.Вот такой массив $_POST и $_FILES у меня получился при распечатке через print_r():

	Array ( 
	  [title] => test 
	  [bodytext] => asdfasf 
	) 
	Array (
 	  [filename] => Array ( 
	    [name] => ip.txt 
		[type] => text/plain 
		[tmp_name] => Y:\tmp\php2C5.tmp 
		[error] => 0 [size] => 13 
	  ) 
	  [image] => Array (
  	    [name] => Coat_of_arms_of_Vladimiri_Oblast.png 
		[type] => image/png 
		[tmp_name] => Y:\tmp\php2C6.tmp 
		[error] => 0 [size] => 393743 
	  ) 
	) 

Нужно еще изменить запрос вставки сообщения:

 $sql = 'INSERT INTO Messages (title, bodytext, created, fid, image_fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID()-1, LAST_INSERT_ID())';
 

Для определения fid обычного файла и изображения я использую функцию LAST_INSERT_ID(), так как обычный файл вставляет перед изображением, то я отнимаю единицу от последнего id.
Теперь когда изображения вставляются в БД, мы можем выводить эти изображения. Но для работы с изображениями мы сделаем заготовку, отдельный класс, который назовем simpleImage. Создадим новый файл simpleImage.php в папке class и там будем писать код нашего класса.
Открываем файл simpleImage.php и пишем в нем:

<?php

class simpleCMS {

}

?>

Итак, мы создали новый класс для работы с изображениями, теперь нужно будет создать методы для работы с ним. Но сначала я расскажу о видах переменных класса в PHP.

Свойства класса в PHP 5

Раньше в PHP4 все было просто, свойства класса определялись как var, а вот в 5ой версии PHP появилось больше свободы в определение свойств класса. Начнем с оператора public, потому что мы его уже использовали.

Public

Через оператор public мы заявляем открытую, публично доступную переменную класса. Таким образом мы можем менять значение этой переменной в созданном объекте, просто присвоить нужное нам значение, например так:

<?php
  $obj = new simpleCMS(); 
  $obj->db='newDB';
?>

Так мы можем обращаться к переменной объекта, еще раз, потому что переменная была определена как public.

Protected

Оператор protected показывает, что свойство или метод доступно внутри класса или из его производного класса (о наследование и производных классах мы поговорим позже). Если мы определим сейчас свойство db как protected и вызовем это свойство в коде, то это вызовет ошибку:

simpleCMS.php
...
protected $db = 'testDB';
...

index.php
...
$obj = new simpleCMS(); 
$obj->db='newDB';
...

Давайте теперь добавим защищенную переменную path (путь к файлу), для того чтобы потом использовать ее в методах класса. Но для того чтобы передавать путь к файлу мы будем использовать конструктор.

Конструктор класса

Конструктор класса используется в PHP для того чтобы задать начальное состояние создаваемому объекту, например мы будем задавать путь к файлу для создаваемого объекта изображения. Конструктор записывается как и все остальные методы, в начале его именни два нижних подчеркивания:

class simpleCMS {
  protected $path;
  
  public __construct($image){
    $this->path = $image;
  }
}

Таким образом мы передаем путь к файлу через параметр конструктора и записываем в защищенную переменную пути к изображению. Теперь нужно изменить метод public_display для класса simpleCMS, чтобы выводились данные о изображениях.

  if(!empty($row['image_fid'])){
	$sql = "SELECT * FROM Files WHERE fid=".$row['image_fid'];
	$image_query = mysql_query($sql) or die(mysql_error());  
	$image = mysql_fetch_array($image_query);
	$content .= '<div class="image">';
	  $image = new simpleImage($image['filepath']);
	  $content .= $image->scale(100,100);
	$content .= '</div>';
	unset($image);
  }

Так мы выводим из БД путь к файлу. Теперь, когда мы вывели путь к файлу, давайте писать методы для обработки размеров изображения. Сделаем функции как в друпале:

public function scale($width,$height){

}
public function scale_and_crop($width,$height){

}

Давайте начнем с функции масштабирования scale().

public function scale($width, $height){
    $path = $this->path;
	$filepath = explode('/',$this->path); //разделим папки и имя файла
	$filename = end($filepath); // забираем последний элемент - имя файла
	$filename = substr($filename, 0, -4); //убираем из имени файла расширение
	$filename = str_replace(' ', '_', $filename); //убираем пробелы из имени файла
    $extenstion = substr($this->path, -4); // проверяем расширение файла png, gif, jpg
	
	switch ($extenstion){
	  case '.png':
	    $srcImage = ImageCreateFromPNG( $path );
	  break;
	  case '.jpg':
	    $srcImage = ImageCreateFromJPEG( $path );
	  break;
	  case '.gif':
	    $srcImage = ImageCreateFromGIF( $path );
	  break;
	  default:
	    $srcImage = ImageCreateFromGIF( $path ); 
	  break;
	}
	// определяем изначальную высоту и ширину картинки
	$srcWidth = ImageSX( $srcImage );
	$srcHeight = ImageSY( $srcImage );
	// следующий код проверяет если ширина больше высоты
	// или высота больше ширины картинки так, чтобы
	// при изменении сохранилась правильная пропорция
	$ratioWidth = $srcWidth/$width;
	$ratioHeight = $srcHeight/$height;
	if( $ratioWidth < $ratioHeight){
		$destWidth = $srcWidth/$ratioHeight;
		$destHeight = $height;
	}else{
		$destWidth = $width;
		$destHeight = $srcHeight/$ratioWidth;
	}	
	// создаем новую картинку с конечными данными ширины и высоты
	print $destWidth. '<br />' .$destHeight;
	$destImage = imagecreate( $destWidth, $destHeight);
	// копируем srcImage (исходная) в destImage (конечную)
	ImageCopyResized( $destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight );
	//создаем jpeg
	imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); //необходимо создать папку presets в папке files
	$newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg';
	// освобаждаем память
	ImageDestroy( $srcImage );
	ImageDestroy( $destImage );	
	return '<img src="'. $newImagePath . '" width="'. $destWidth.'" height="'. $destHeight . '" />'; // выводим изображение
  }

Файлы обработанных изображений мы будем хранить в папке files/presets, нужно чтобы папка presets была создана и на нее были права доступа 777.

Думаю, что я подробно описал алгоритм работы в комментариях, поэтому давайте перейдем к методу scale_and_crop(), правда он мало чем отличается от scale():

public function scale_and_crop($width, $height){
    $path = $this->path;
	$filepath = explode('/',$this->path); //разделим папки и имя файла
	$filename = end($filepath); // забираем последний элемент - имя файла
	$filename = substr($filename, 0, -4); //убираем из имени файла расширение
	$filename = str_replace(' ', '_', $filename); //убираем пробелы из имени файла
    $extenstion = substr($this->path, -4); // проверяем расширение файла png, gif, jpg
	
	switch ($extenstion){
	  case '.png':
	    $srcImage = ImageCreateFromPNG( $path );
	  break;
	  case '.jpg':
	    $srcImage = ImageCreateFromJPEG( $path );
	  break;
	  case '.gif':
	    $srcImage = ImageCreateFromGIF( $path );
	  break;
	  default:
	    $srcImage = ImageCreateFromGIF( $path ); 
	  break;
	}
	// определяем изначальную высоту и ширину картинки
	$srcWidth = ImageSX( $srcImage );
	$srcHeight = ImageSY( $srcImage );
	// следующий код проверяет если ширина больше высоты
	// или высота больше ширины картинки так, чтобы
	// при изменении сохранилась правильная пропорция
	$ratioSource = $srcWidth/$srcHeight;
	$ratioNew = $width/$height;
	if( $ratioSource < $ratioNew){
		$newWidth = $srcWidth;
		$newHeight = $srcHeight / $ratioNew  * $ratioSource; // расчитываем новую высоту изображения пропорционально заданной ширине
	}else{
		$newWidth = $srcWidth / $ratioSource * $ratioNew; // рассчитываем новую ширину изображения пропорционально заданной высоте
		$newHeight = $srcHeight;	
	}	
	// создаем новую картинку с конечными данными ширины и высоты
	$destImage = imagecreate($width, $height); // отличие от scale()
	// копируем srcImage (исходная) в destImage (конечную)
	imagecopyresampled( $destImage, $srcImage, 0, 0, 0, 0, $width, $height, $newWidth, $newHeight);
	//создаем jpeg
	imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); //необходимо создать папку presets в папке files
	$newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg';
	// освобаждаем память
	ImageDestroy( $srcImage );
	ImageDestroy( $destImage );	
	return '<img src="'. $newImagePath . '" />'; // выводим изображение
  }  

Также нужно будет поменять строку вывода в методе display_public():

$content .= '<div class="image">';
  $image = new simpleImage($image['filepath']);
  $content .= $image->scale_and_crop(100,100);
$content .= '</div>';

Таким образом картинки будут квадратными и обрезанными, чтобы соблюдать пропорции.

Как в первом, так и во втором случаях картинки генерировались на лету. Генерация картинок занимает время поэтому обычно пути к коротким картинкам запоминают в БД и выводят сразу эти пути, без повторной генерации изображений.

Мы не будем в этом курсе рассматривать как это делать, потому что в построение своей CMS мы будем использовать Zend framework, а эти уроки PHP в первую очередь должны познакомить вас с основами работы PHP.