logo

Paleta - Deixe colorido🎨

Palette — Construtor visual de páginas. Não precisa ser designer.

Demonstração ao vivo Baixar Palette

Scroll

Lições de PHP - Lição 4 - Trabalhando com Imagens, Biblioteca GD2.

08/12/2019, by Ivan

Nas lições anteriores, descobrimos como escrever consultas ao banco de dados, então agora daremos menos atenção a como escrever consultas e apenas treinaremos escrevê-las. Também combinaremos a escrita de consultas com o aprendizado de outros recursos do PHP, começando com o processamento de imagens. Em uma das lições anteriores, já carregamos arquivos e temos até uma tabela Arquivos para arquivos carregados. Vamos carregar imagens nesta mesma tabela. Mas primeiro precisamos adicionar um campo para fazer upload de foto no formulário de criação de conteúdo.

$content .= '<label for="bodytext">Mensagem:</label><br />';
$content .= '<textarea name="bodytext" id="bodytext"></textarea><br />';
$content .= '<label for="bodytext">Anexar arquivo:</label><br /><input type="file" name="filename"><br />'; // campo de upload de arquivo
$content .= '<label for="bodytext">Anexar imagem:</label><br /><input type="file" name="image">'; // campo de upload de arquivo

Agora você precisa escrever o processamento de arquivos, mas primeiro adicione uma coluna na tabela Mensagens onde registraremos o fid das imagens, e os dados de imagem registraremos na tabela Arquivos, como com outros arquivos. Chamo a coluna de image_fid suas características são as seguintes INT(11) e NULL por padrão. Agora vamos inserir o processamento do formulário no método write():

 if($_FILES["image"]["size"] > 1024*3*1024){
       echo ("O tamanho do arquivo excede três megabytes");
       exit;
    }
   if(is_uploaded_file($_FILES["image"]["tmp_name"])){    // Verificar se o arquivo foi carregado
     // Se o arquivo foi carregado com sucesso, mova-o
     // do diretório temporário para o final
     move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]);
   } else {
      echo("Erro ao carregar arquivo");
   }

	$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);  

Este processamento não diferirá em nada do carregamento de arquivos comuns, embora em uma das próximas lições analisaremos como fazer uma verificação de qual tipo de arquivo está sendo carregado, para que pdf-documentos não sejam carregados sob pretexto de imagens.

Escrevemos o processamento, agora dois arquivos serão carregados, um dos quais é uma imagem. Aqui está o array $_POST e $_FILES que obtive ao imprimir com print_r():

	Array (
		  [title] => teste
		  [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
		  )
		) 

Você ainda precisa alterar a solicitação de inserção de mensagem:

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

Para determinar o fid do arquivo regular e da imagem, uso a função LAST_INSERT_ID(), já que o arquivo regular é inserido antes da imagem, subtraio um do último id.
Agora que as imagens estão sendo inseridas no banco de dados, podemos exibir essas imagens. Mas para trabalhar com imagens, faremos um modelo, uma classe separada, que chamaremos de simpleImage. Vamos criar um novo arquivo simpleImage.php na pasta class e aí escreveremos o código da nossa classe.
Abra o arquivo simpleImage.php e escreva nele:

<?php

class simpleCMS {

}

?>

Assim, criamos uma nova classe para trabalhar com imagens, agora precisamos criar métodos para trabalhar com ela. Mas primeiro vou contar sobre os tipos de variáveis de classe em PHP.

Propriedades de Classe em PHP 5

Antes em PHP 4 tudo era simples, as propriedades da classe eram definidas como var, mas na versão 5 do PHP havia mais liberdade na definição das propriedades da classe. Vamos começar com o operador public, porque já o usamos.

Public

Através do operador public declaramos uma variável de classe aberta e publicamente acessível. Assim, podemos alterar o valor dessa variável no objeto criado, simplesmente atribuindo o valor desejado, por exemplo assim:

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

Assim podemos acessar a variável do objeto, mais uma vez, porque a variável foi definida como public.

Protected

O operador protected mostra que a propriedade ou método é acessível dentro da classe ou de sua classe derivada (falaremos sobre herança e classes derivadas mais tarde). Se definirmos agora a propriedade db como protected e chamarmos essa propriedade no código, isso causará um erro:

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

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

Agora vamos adicionar uma variável protegida path (caminho do arquivo), para que possamos usá-la depois nos métodos da classe. Mas para passar o caminho do arquivo, usaremos o construtor.

Construtor de Classe

O construtor de classe é usado em PHP para definir o estado inicial do objeto a ser criado, por exemplo, definiremos o caminho do arquivo para o objeto de imagem criado. O construtor é escrito como qualquer outro método, no início do seu nome há dois sublinhados:

class simpleCMS {
  protected $path;

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

Assim, passamos o caminho do arquivo através do parâmetro do construtor e o registramos em uma variável protegida do caminho para a imagem. Agora você precisa alterar o método public_display para a classe simpleCMS para exibir dados sobre imagens.

  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);
  }

Assim extraímos o caminho do arquivo do banco de dados. Agora que extraímos o caminho do arquivo, vamos escrever os métodos para processar os tamanhos da imagem. Faremos funções como no Drupal:

public function scale($width,$height){

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

}

Vamos começar com a função de escala scale().

public function scale($width, $height){
    $path = $this->path;
	$filepath = explode('/',$this->path); //dividir pastas e nome de arquivo
	$filename = end($filepath); // pegue o último elemento - nome do arquivo
	$filename = substr($filename, 0, -4); //remova a extensão do nome do arquivo
	$filename = str_replace(' ', '_', $filename); //remova espaços do nome do arquivo
    $extenstion = substr($this->path, -4); // verifique a extensão do arquivo 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;
	}
	// determine a altura e largura originais da imagem
	$srcWidth = ImageSX( $srcImage );
	$srcHeight = ImageSY( $srcImage );
	// o código seguinte verifica se a largura é maior que a altura
	// ou a altura é maior que a largura da imagem de forma que
	// ao mudar de tamanho a proporção correta seja mantida
	$ratioWidth = $srcWidth/$width;
	$ratioHeight = $srcHeight/$height;
	if( $ratioWidth  $ratioHeight){
		$destWidth = $srcWidth/$ratioHeight;
		$destHeight = $height;
	}else{
		$destWidth = $width;
		$destHeight = $srcHeight/$ratioWidth;
	}
	// criar uma nova imagem com dados finais de largura e altura
	print $destWidth. '<br />' .$destHeight;
	$destImage = imagecreate( $destWidth, $destHeight);
	// copie srcImage (original) em destImage (final)
	ImageCopyResized( $destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight );
	//criar jpeg
	imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); //você precisa criar a pasta presets dentro da pasta files
	$newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg';
	// liberar memória
	ImageDestroy( $srcImage );
	ImageDestroy( $destImage );
	return '<img src="'. $newImagePath . '" width="'. $destWidth.'" height="'. $destHeight . '" />'; // exibir imagem
  }

Armazenaremos arquivos de imagem processados na pasta files/presets, a pasta presets precisa ser criada e ter permissões de acesso 777.

Acho que descrevi o algoritmo em detalhes nos comentários, então vamos passar para o método scale_and_crop(), embora seja pouco diferente de scale():

public function scale_and_crop($width, $height){
    $path = $this->path;
	$filepath = explode('/',$this->path); //dividir pastas e nome de arquivo
	$filename = end($filepath); // pegue o último elemento - nome do arquivo
	$filename = substr($filename, 0, -4); //remova a extensão do nome do arquivo
	$filename = str_replace(' ', '_', $filename); //remova espaços do nome do arquivo
    $extenstion = substr($this->path, -4); // verifique a extensão do arquivo 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;
	}
	// determine a altura e largura originais da imagem
	$srcWidth = ImageSX( $srcImage );
	$srcHeight = ImageSY( $srcImage );
	// o código seguinte verifica se a largura é maior que a altura
	// ou a altura é maior que a largura da imagem de forma que
	// ao mudar de tamanho a proporção correta seja mantida
	$ratioSource = $srcWidth/$srcHeight;
	$ratioNew = $width/$height;
	if( $ratioSource  $ratioNew){
		$newWidth = $srcWidth;
		$newHeight = $srcHeight / $ratioNew  * $ratioSource; // calcule a nova altura da imagem proporcionalmente à largura especificada
	}else{
		$newWidth = $srcWidth / $ratioSource * $ratioNew; // calcule a nova largura da imagem proporcionalmente à altura especificada
		$newHeight = $srcHeight;
	}
	// criar uma nova imagem com dados finais de largura e altura
	$destImage = imagecreate($width, $height); // diferença de scale()
	// copie srcImage (original) em destImage (final)
	imagecopyresampled( $destImage, $srcImage, 0, 0, 0, 0, $width, $height, $newWidth, $newHeight);
	//criar jpeg
	imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); //você precisa criar a pasta presets dentro da pasta files
	$newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg';
	// liberar memória
	ImageDestroy( $srcImage );
	ImageDestroy( $destImage );
	return '<img src="'. $newImagePath . '" />'; // exibir imagem
  }  

Você também precisará alterar a linha de saída no método display_public():

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

Assim as imagens serão quadradas e cortadas para manter as proporções.

Nos dois casos, as imagens eram geradas dinamicamente. A geração de imagens leva tempo, portanto, normalmente os caminhos para imagens curtas são lembrados no banco de dados e esses caminhos são exibidos imediatamente, sem regeneração de imagens.

Não consideraremos neste curso como fazer isso, porque ao construir nosso próprio CMS usaremos o Zend Framework, e essas lições de PHP devem, em primeiro lugar, apresentá-lo aos conceitos básicos do PHP.