Extra Block Types (EBT) - Nueva experiencia con Layout Builder❗

Extra Block Types (EBT): tipos de bloques con estilo y personalizables: Presentaciones de diapositivas, Pestañas, Tarjetas, Acordeones y muchos más. Configuraciones integradas para fondo, DOM Box y plugins de JavaScript. Experimenta hoy el futuro de la construcción de diseños.

Módulos de demostración EBT Descargar módulos EBT

❗Extra Paragraph Types (EPT) - Nueva experiencia con Paragraphs

Extra Paragraph Types (EPT): conjunto de módulos basado en párrafos de forma análoga.

Módulos de demostración EPT Descargar módulos EPT

Scroll

Lecciones de PHP - Lección 4 - Trabajo con imágenes, biblioteca GD2.

10/05/2025, by Ivan

En las lecciones anteriores aprendimos cómo escribir consultas a la base de datos, por lo tanto ahora prestaremos menos atención a cómo escribirlas y simplemente practicaremos. También combinaremos la escritura de consultas con el estudio de otras funcionalidades de PHP, comenzando con el procesamiento de imágenes. En una de las lecciones anteriores ya subimos archivos y tenemos incluso una tabla Files para archivos cargados. Vamos a subir imágenes en esta misma tabla. Pero primero hay que añadir un campo para subir una foto en el formulario de creación de material.

$content .= '<label for="bodytext">Mensaje:</label><br />';
$content .= '<textarea name="bodytext" id="bodytext"></textarea><br />';
$content .= '<label for="bodytext">Adjuntar archivo:</label><br /><input type="file" name="filename"><br />'; // campo de subida de archivo
$content .= '<label for="bodytext">Adjuntar imagen:</label><br /><input type="file" name="image">'; // campo de subida de imagen

Ahora hay que escribir la lógica de manejo de archivos, pero primero agreguemos otra columna en la tabla Messages donde guardaremos el fid de las imágenes, y los datos de la imagen se guardarán en la tabla Files como el resto de los archivos. Llamé a la columna image_fid con las siguientes características: INT(11) y NULL por defecto. Ahora vamos a insertar el manejo del formulario en el método write():

if($_FILES["image"]["size"] > 1024*3*1024){
    echo ("El tamaño del archivo supera los tres megabytes");
    exit;
}
if(is_uploaded_file($_FILES["image"]["tmp_name"])){    // Verifica si el archivo fue subido
    // Si el archivo se subió exitosamente, lo movemos
    // desde el directorio temporal al destino final
    move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]);
} else {
    echo("Error al subir el archivo");
}	

$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 manejo no será diferente al de los archivos comunes, aunque en una lección futura veremos cómo validar el tipo de archivo para evitar que se suban documentos como PDF en lugar de imágenes.

Ya hemos escrito el procesamiento, ahora se subirán dos archivos, uno de ellos será una imagen. Así es como lucen los arrays $_POST y $_FILES al imprimirlos con 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 
  ) 
)

También hay que modificar la consulta de inserción del mensaje:

$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 el fid del archivo común y de la imagen usamos la función LAST_INSERT_ID(), ya que el archivo común se inserta antes que la imagen, entonces restamos uno del último id.
Ahora que las imágenes se insertan en la base de datos, podemos mostrarlas. Para trabajar con imágenes, crearemos una estructura básica, una clase separada llamada simpleImage. Crearemos un nuevo archivo simpleImage.php en la carpeta class y allí escribiremos el código de nuestra clase.
Abrimos el archivo simpleImage.php y escribimos:

<?php

class simpleCMS {

}

?>

Así creamos una nueva clase para trabajar con imágenes, ahora necesitamos crear métodos para trabajar con ella. Pero primero explicaré los tipos de variables de clase en PHP.

Propiedades de clase en PHP 5

Antes, en PHP4 todo era simple, las propiedades se definían como var, pero en PHP5 hay más libertad para definir propiedades. Empecemos con el modificador public, ya que lo hemos usado antes.

Public

Con el modificador public declaramos una variable de clase de acceso público. Esto permite cambiar su valor directamente desde el objeto creado, así:

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

Así accedemos a la variable del objeto porque fue definida como public.

Protected

El modificador protected indica que la propiedad o método sólo es accesible desde la clase o sus clases derivadas (más adelante hablaremos de herencia). Si ahora definimos la propiedad db como protected e intentamos acceder a ella como arriba, obtendremos un error:

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

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

Ahora agregaremos una propiedad protegida path (ruta al archivo) para luego usarla en los métodos de la clase. Para pasar la ruta usaremos un constructor.

Constructor de clase

El constructor de clase en PHP se usa para establecer el estado inicial de un objeto. En este caso pasaremos la ruta del archivo al crear el objeto de imagen. El constructor se define como cualquier otro método, iniciando con dos guiones bajos:

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

Así pasamos la ruta del archivo como parámetro del constructor y la almacenamos en la propiedad protegida de ruta. Ahora modificamos el método public_display de la clase simpleCMS para mostrar las imágenes.

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

Así mostramos desde la base de datos la ruta al archivo. Ahora, teniendo la ruta al archivo, escribiremos métodos para ajustar el tamaño de la imagen. Haremos funciones similares a las de Drupal:

public function scale($width,$height){

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

}

Comencemos con la función de escalado scale().

Continuemos con la función de escalado scale().

public function scale($width, $height){
    $path = $this->path;
    $filepath = explode('/', $this->path); // dividimos la ruta en carpetas y nombre de archivo
    $filename = end($filepath); // tomamos el último elemento - el nombre del archivo
    $filename = substr($filename, 0, -4); // quitamos la extensión del nombre
    $filename = str_replace(' ', '_', $filename); // reemplazamos espacios con guiones bajos
    $extenstion = substr($this->path, -4); // verificamos la extensión: 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);
    ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);

    imagejpeg($destImage, 'files/presets/' . $width . '_' . $height . '_' . $filename . '.jpg', 100);
    $newImagePath = 'files/presets/' . $width . '_' . $height . '_' . $filename . '.jpg';

    ImageDestroy($srcImage);
    ImageDestroy($destImage);

    return '<img src="' . $newImagePath . '" width="' . $destWidth . '" height="' . $destHeight . '" />';
}

Guardaremos los archivos de imágenes procesadas en la carpeta files/presets, asegúrate de crear esa carpeta y asignarle permisos 777.

Creo que el algoritmo está explicado suficientemente en los comentarios, así que pasemos al método scale_and_crop(), que se diferencia poco del anterior:

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

    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);
    imagecopyresampled($destImage, $srcImage, 0, 0, 0, 0, $width, $height, $newWidth, $newHeight);

    imagejpeg($destImage, 'files/presets/' . $width . '_' . $height . '_' . $filename . '.jpg', 100);
    $newImagePath = 'files/presets/' . $width . '_' . $height . '_' . $filename . '.jpg';

    ImageDestroy($srcImage);
    ImageDestroy($destImage);

    return '<img src="' . $newImagePath . '" />';
}

También hay que modificar la línea de salida en el método display_public():

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

Así las imágenes se mostrarán como cuadrados recortados, manteniendo las proporciones.

Tanto en scale() como en scale_and_crop() las imágenes se generan en tiempo real. Generar imágenes requiere tiempo, por lo que normalmente se almacenan las rutas a las miniaturas en la base de datos y se reutilizan sin volver a generar cada vez.

En este curso no abordaremos cómo hacerlo porque al construir nuestra propia CMS usaremos el Zend Framework. Estas lecciones están orientadas principalmente a familiarizarte con los fundamentos del trabajo en PHP.