PHP Graphic CMS Class with Template Method

uploadLoad Up the Graphic Files

In Part IV of the Content Management System (CMS) series, I noted that I wanted to have a separate post on uploading graphics to a web server. This post addresses the issues involved in getting graphics in place on a web server that will be included in a Web page, whether you’re using a CMS or not. In the case of selecting a graphic from your desktop and uploading it to a server, most of the important work is done by HTML. So, we’ll start with the HTML assuming that HTML5 is in use. Be advised at the outset that you will need to do a little system administration to set your file privileges in the folders where you will store the graphic files. In the development process, I used a local host, a different computer on my LAN, and two different web servers at remote locations.

Choosing a File to Upload

HTML5 (and possibly earlier versions) has some very nice built-in form attributes that make selecting a graphic file from your computer to place in an “uploadable” state quite easy. All you need are a couple of lines of HTML. Within a form container, the line:

<input type=”file” name=”file” id=”file”>

generates the following button when you load the code into a browser:

In the <form> tag, you will need to include an enctype assignment:

<form action=”target.php” method=”post” enctype=”multipart/form-data”>

That’s about it, and you’re all set to select a file that can be accessed through the PHP superglobal variable $_FILES.

The following HTML code generates the CSS first the then HTML portion of the little graphic CMS:

@charset "UTF-8";
/* CSS Document */
/* upload.css */
body
{
    font-family: Verdana, helvetica, arial, futura, sans-serif;
    color:#991C00;
    background-color:#FFFCDB; 
}
 
h1
{
    font-family: "Arial Black", futura, sans-serif;
    color:#E09A25;
    background-color:#262B30;
    text-align: center;
}
<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
            <link type="text/css" rel="stylesheet" href="upload.css">
        <title>Catalog Files</title>
    </head>
<body>
    <h1>Graphic File Loader</h1>
    <form action="triggerUpload.php" method="post" enctype="multipart/form-data">
    Click the 'Choose File' button to browse your local drive for files to upload:<br/>
        <input type="file" name="file" id="file"><p/>
    Once you have chosen a file, click the 'Load Graphic File' button to upload the file to your web server:<br/>
        <input type="submit" name="submit" value="Load Graphic File">
    </form>
</body>
</html>

I added separation between the Choose File button and Load Graphic File submit button so that the HTML and PHP portions are more clearly defined. Figure 1 shows the initial web page for the upload:

Figure 1: Selecting and calling PHP Script

Figure 1: Selecting and calling PHP Script

Since the goal of having a CMS is to make updates simple, this initial example does not have any whistles and bells (e.g., folder selection), and it does nothing more than to prepare a selected file for upload to a server.

A Little PHP

On the PHP side of the equation, you have only two code elements:

  1. $_FILES: Superglobal array with selected file information in elements
  2. move_uploaded_file(): Stores uploaded file (in $_FILES) to in specified server location.

Creating a class, using the minimum code and no error, type or size checking, is quite simple. To keep it even simpler, the following trigger script instantiates the Upload class:

<?php
//triggerUpload.php
include_once("Upload.php");
$worker = new Upload();
?>

After testing the initial Upload class, we’ll see how it can be enhanced. First, though, take a look at the code:

<?php
//Upload.php
class Upload
{
    private $fileNow;
    public function __construct()
{
        if ($_FILES)
        {
            $this->fileNow=$_FILES["file"]["name"];
 
            //The following line does all the work
            move_uploaded_file($_FILES["file"]["tmp_name"],"images/$this->fileNow");
 
            echo $this->fileNow . " has been uploaded to the images folder:";
        }
    }
}
?>

That simple class does everything you need to upload and store a file in a folder named “images” in the directory where the script is launched. It’s pretty basic, but you should be able to see exactly what is required to upload a file to a web server without having to use an FTP client.

Template Method for Details

Note: Before you get started on this section, download all of the files that are involved:
Download

As you saw, the Upload class is quite easy. However, you will want to make sure that not just any file type is loaded and you’ll want to make sure that you don’t have giant files loaded that may fill up your disk space. (You also want to protect against maliciously uploaded files.) The $_FILES array has information to use for checking type and size. Using the $_FILES array information we can boil the process down to three steps:

  1. checkSize(); See how big it is and compare to a maximum allowable size.
  2. checkType(); Add a set of acceptable file types used with a Web page and check to see if the file type is acceptable.
  3. uploadFile(); If the file type and size are okay, upload the file.

Click below to continue:

Further, we’ll say that we want those steps in that order so that we don’t accidentally load a file of the wrong size or type. Given those conditions, this sounds like a job for a Template Method: the template method sets the order in an abstract class, and then the implementation provides the content. In the abstract class, we can also add the properties and even add default values to some where the initial values are known.

<?php
//LoadTemplate.php
abstract class LoadTemplate
{
   abstract protected function checkSize();
   abstract protected function checkType();
   abstract protected function uploadFile(); 
 
    protected function templateMethod()
    {
            $this->checkSize();
            $this->checkType();
            $this->uploadFile(); 
    }
 
    protected  $fileProblem;
    protected  $fileNow;
    protected  $imgSize;
    protected  $imgType;
    protected  $sizeApproved = True;
    protected  $typeApproved = True;
}
?>

All of the methods and properties, both implemented and abstract, are set to protected so that they can be inherited and used by the Upload class. The template method is named templateMethod() to help identify it, but it could have been named anything. In the template method are three abstract methods, suggesting that this abstract class could be used with lots of different implementations and easily reused in a different project.

Updating the Upload Class

Now, the Upload class will inherit the abstract LoadTemplate class and implement the abstract and unassigned properties. As a child class, the Upload class simply adds the templateMethod(), which has already been implemented in the LoadTemplate class. Importantly, the Upload class cannot change the order of the methods in template method. Notice, unlike the other examples on this blog of using the Template Method design pattern, this one is called in the middle of a conditional statement. As you will see, it works perfectly well. It’s not so much where the method is called but how the methods that make up the template method have been implemented.

<?php
//Upload.php
include_once('LoadTemplate.php');
class Upload extends LoadTemplate
{
    const FILEMAX=1000000;
 
    public function __construct()
    {        
        if ($_FILES['file']['error'] == 0)
        {
            $this->fileNow=$_FILES["file"]["name"];
            $this->templateMethod();
        }
        else
        {
            echo $this->fileError($_FILES['file']['error']);
        }
    }
    protected function checkSize()
    {
         $this->imgSize=$_FILES["file"]["size"];
         if($this->imgSize > self::FILEMAX)
         {
            $this->sizeApproved = False;
         }
    }
 
    protected function checkType()
    {
         $this->imgType=$_FILES["file"]["type"];
         switch($this->imgType)
         {
            case "image/png":
                break;
            case "image/jpeg":
                break;
            case "image/gif":
                break;
            case "image/svg+xml":
                break;
            default:
                $this->typeApproved=False;
                break;
         }
    }
 
    protected function uploadFile()
    {
         if($this->sizeApproved && $this->typeApproved)
         {
            move_uploaded_file($_FILES["file"]["tmp_name"],"images/$this->fileNow"); 
            echo "The file $this->fileNow has been uploaded to the images folder:<br />";
         }
         elseif(! $this->sizeApproved)
         {
                echo "Your file was too big.<br />It must be less than 1mb.";
        }
        elseif(! $this->typeApproved)
        {
                echo "Your file type cannot be loaded into a Web page.<br />It must be a jpg, png, gif, or svg file.";
        }
 
    }
 
    private function fileError($error)
    {
        $errorNow = $error;
        switch($errorNow)
        {
            case 1:
                $this->fileProblem= "UPLOAD_ERR_INI_SIZE: File exceeded the php.ini upload maximum for file size.";
                break;
            case 2:
                $this->fileProblem= "UPLOAD_ERR_FORM_SIZE: File exceeded the maximum file size specified in HTML form.";
                break;
            case 3:
                $this->fileProblem= "UPLOAD_ERR_PARTIAL: File was only partially uploaded. Try again.";
                break;
            case 4:
                $this->fileProblem= "UPLOAD_ERR_NO_FILE: No file was uploaded. Be sure to first select file.";
                break;
            case 6:
                $this->fileProblem= "UPLOAD_ERR_NO_TMP_DIR: Missing a temporary folder.";
                break;
            case 7:
                $this->fileProblem= "UPLOAD_ERR_CANT_WRITE: Failed to write file to disk. Check permissions for directory you're writing to.";
                break;
            case 8:
                $this->fileProblem= "UPLOAD_ERR_EXTENSION: A PHP extension stopped the file upload.";
                break; 
        }
        return $this->fileProblem;
    }
}
?>

The changes in the Upload class from the original one used at the beginning of this post are actually very small. Upload now has error checking and checks for size and file type, but the real work is still done by the single move_uploaded_file() method. So while there’s a lot you can do in setting parameters for the files you upload as part of a CMS, the upload part is made simpler by the features of both HTML and PHP.

The Problems with $_FILES

While the superglobal $_FILES is the source of file information, the information generated can be unreliable. For example, while it can recognize files with a .jpg extension (in lowercase), it cannot correctly recognize a jpeg file with the uppercase extension (.JPG). However, it does not have that problem with .GIF files. In order to test the limitations of this superglobal, in the download package, I’ve included several different types of files to test. They are in a in a folder named “graphics” and can be placed on your desktop for experimentation.

Setting Permissions

If you’ve never set permissions for folders/directories before, I strongly suggest that you use your localhost or LAN PHP server before you go about changing permissions on production servers. (I spent almost a day of back and forth with my hosting service so that I could change the permissions to what I needed.) All you need to do is to change the permissions from Read only to Write only for everyone on your localhost. Also, you only need to do it for your “images” folder and other folders that you wish to upload files. Testing on remote and local servers I found that the UNIX value of 733 works. It allows writing and executing but not reading. General settings on one remote server is 755, which is fairly typical, but it will not allow you to upload a graphic to such folders using a PHP page. These settings can be found in the “properties” of Windows and “info” on Macs; however, depending on the version of OS that you have, the nature of making changes may vary. In order to keep out of trouble, check with your hosting service, and when you make changes, only make the changes to a single folder where you plan to upload your graphic files. Online and in books, articles and manuals you will find lots of information about changing permissions for your localhost on Windows and Macs. Likewise, you can find many dangers in opening write permissions, and you should heed those warnings.

Share

Copyright © 2013 William Sanders. All Rights Reserved.

0 Responses to “PHP Graphic CMS Class with Template Method”


  • No Comments

Leave a Reply