SitePoint Sponsor

User Tag List

Results 1 to 25 of 25
  1. #1
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    File Upload Class

    After searching around for a while, I could not find any reusable file uploading code that was done well. That's what prompted me to write this. I'm very open to comments, suggestions, criticisms, critiques, etc. (That's why I'm posting this code here!)

    The classes:
    PHP Code:
    class FileUpload
    {
        var 
    $mUploadDir;            // Directory to which files will be uploaded
        
    var $mValidExt;             // Allowed (valid) file extensions
        
    var $mAllowAllExt;          // Flag to allow any file extensions to go through
        
    var $mFiles;                // Instantiated FileDesc objects
        
    var $mErrorMsgs;            // Error messages

        
    function FileUpload()
        {
            
    $this->mUploadDir       '';
            
    $this->mValidExt        = array();
            
    $this->mAllowAllExt     true;
            
    $this->mFiles           = array();
            
    $this->mErrorMsgs       = array();

            
    $this->mErrorMsgs[0]    = 'There is no error, the file uploaded with success.';
            
    $this->mErrorMsgs[1]    = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
            
    $this->mErrorMsgs[2]    = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.';
            
    $this->mErrorMsgs[3]    = 'The uploaded file was only partially uploaded.';
            
    $this->mErrorMsgs[4]    = 'No file was uploaded.';
            
    $this->mErrorMsgs[5]    = 'The uploaded file is of a type that is not currently allowed.';
            
    $this->mErrorMsgs[6]    = 'The uploaded file exceeds the maximum allowable file size.';
            
    $this->mErrorMsgs[7]    = 'The uploaded file could not be moved to the specified directory.';
        }

        
    //
        // AddFile -- Adds a file to be uploaded from a $_FILES element
        // object AddFile ( array file)

        
    function & AddFile($fileArray)
        {
            
    $fd = new FileDesc;

            
    $fd->SetFromFileArray($fileArray);
            return (
    $this->mFiles[] = $fd);
        }

        
    //
        // ProcessUpload -- Uploads files that have been added
        // bool ProcessUpload ( void )

        
    function ProcessUpload()
        {
            
    // Prepare upload directory for writing

            
    $this->PrepUploadDir();

            foreach (
    $this->mFiles as $key => $file)
            {
                
    // The error code is something other than the success code (0)

                
    if (!= $file->mError)
                {
                    return 
    false;
                }

                
    // Not all extensions should be allowed, so check the extension

                
    if (false == $this->mAllowAllExt)
                {
                    
    // Compare the file extension to the array of valid extensions
                    // File extension is taken from the last period to the end of
                    // the file name

                    
    if (!in_array(strtolower(strrchr($file->mFileName'.')), $this->mValidExt))
                    {
                        
    $this->mFiles[$key]->mError 5;
                        return 
    false;
                    }
                }

                
    // A maximum file size has been set

                
    if (!= $file->mMaxFileSize)
                {
                    
    // Verify file size to be within maximum allowed size

                    
    if ($file->mFileSize $file->mMaxFileSize)
                    {
                        
    $this->mFiles[$key]->mError 6;
                        return 
    false;
                    }
                }

                
    // Move the file to the specified upload directory

                
    if (false == @move_uploaded_file($file->mTmpFileName$this->mUploadDir $file->mFileName))
                {
                    
    $this->mFiles[$key]->mError 7;
                    return 
    false;
                }
            }

            return 
    true;
        }

        
    //
        // PrepUploadDir -- Attempts to prepare specified destination directory for writing
        // bool PrepUploadDir ( void )

        
    function PrepUploadDir()
        {
            
    // Attempt to create a new directory if the specified directory
            // doesn't exist

            
    if (false == @file_exists($this->mUploadDir))
            {
                if (
    false == @mkdir($this->mUploadDir0777))
                {
                    return 
    false;
                }
            }

            
    // Attempt to set proper directory permissions if the specified
            // directory cannot be written to

            
    if (false == @is_writable($this->mUploadDir))
            {
                if (
    false == @chmod($this->mUploadDir0777))
                {
                    return 
    false;
                }
            }

            return 
    true;
        }

        function 
    SetUploadDir($uploadDir)
        {
            
    $this->mUploadDir $uploadDir;
        }

        
    //
        // SetValidExt -- Defines acceptable file extensions
        // void SetValidExt ( string ext1 [, string extn...])

        
    function SetValidExt()
        {
            
    // Each parameter will become a value in the mValidExt array

            
    $this->mValidExt func_get_args();
            
    $this->mAllowAllExt false;
        }

        
    //
        // AddValidExt -- Adds an acceptable file extension
        // void AddValidExt ( string ext)

        
    function AddValidExt($validExt)
        {
            
    $this->mValidExt[] = $validExt;
            
    $this->mAllowAllExt false;
        }

        
    //
        // GetErrorMsg -- Gets an unordered list of error messages
        // string GetErrorMsg ( void )

        
    function GetErrorMsg()
        {
            
    $error_msg "<ul>\n";

            foreach (
    $this->mFiles as $file)
            {
                
    // Only add the message if error code is non-zero (success)

                
    if (!= $file->mError)
                {
                    
    $error_msg .= '<li><em>' $file->mFileName '</em>: ';
                    
    $error_msg .= $this->mErrorMsgs[$file->mError] . "</li>\n";
                }
            }

            
    $error_msg .= "</ul>\n";

            return 
    $error_msg;
        }
    }

    class 
    FileDesc
    {
        var 
    $mFileName;             // File name that uploaded file will be saved as
        
    var $mTmpFileName;          // Temporary file name for PHP's use
        
    var $mFileSize;             // Size of uploaded file
        
    var $mMaxFileSize;          // Maximum allowable file size
        
    var $mError;                // Reported error code

        
    function FileDesc()
        {
            
    $this->mFileName        '';
            
    $this->mTmpFileName     '';
            
    $this->mFileSize        0;
            
    $this->mMaxFileSize     0;
            
    $this->mError           null;
        }

        
    //
        // SetFromFileArray -- Sets class variables from a $_FILES element
        // void SetFromFileArray ( array file)

        
    function SetFromFileArray($fileArray)
        {
            
    $this->mFileName $fileArray['name'];
            
    $this->mTmpFileName $fileArray['tmp_name'];
            
    $this->mFileSize $fileArray['size'];
            
    $this->mError $fileArray['error'];
        }

        
    //
        // SetMaxFileSize -- Defines maximum allowable file size
        // void SetMaxFileSize ( int size [, string unit])

        
    function SetMaxFileSize($maxFileSize$sizeUnits 'B')
        {
            switch (
    $sizeUnits)
            {
                case 
    'GB':
                    
    $this->mMaxFileSize $maxFileSize 1024 1024 1024;
                    break;
                case 
    'MB':
                    
    $this->mMaxFileSize $maxFileSize 1024 1024;
                    break;
                case 
    'KB':
                    
    $this->mMaxFileSize $maxFileSize 1024;
                    break;
                case 
    'B':
                    
    $this->mMaxFileSize $maxFileSize;
                    break;
            }
        }

        function 
    SetFileName($fileName)
        {
            
    $this->mFileName $fileName;
        }

        function 
    GetFileName()
        {
            return 
    $this->mFileName;
        }

    An implementation example:
    PHP Code:
    <!DOCTYPE html PUBLIC 
        "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">

    <head>
    <title>FileUpload Class Implementation Example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>

    <body>
    <?php

    require 'class.fileupload.php';

    if (isset(
    $_POST['submit']))
    {
        
    $fu = new FileUpload;
        
        
    $fu->SetUploadDir('C:\Documents and Settings\Matt\Desktop\test\\');
        
    $fu->SetValidExt('.gif''.jpg''.png''.tif''.eps');
        
    $fu->AddValidExt('.bmp');

        
    $file1 =& $fu->AddFile($_FILES['my_file1']);
        
    $file1->SetMaxFileSize(3'MB');

        
    $file2 =& $fu->AddFile($_FILES['my_file2']);
        
    $file2->SetMaxFileSize(800'KB');

        
    $file3 =& $fu->AddFile($_FILES['my_file3']);
        
    $file3->SetMaxFileSize(5242880);

        if (
    false != $fu->ProcessUpload())
        {
            echo 
    $file1->GetFileName() . ' was uploaded successfully.<br />';
            echo 
    $file2->GetFileName() . ' was uploaded successfully.<br />';
            echo 
    $file3->GetFileName() . ' was uploaded successfully.';
        }
        else
        {
            echo 
    $fu->GetErrorMsg();        
        }
    }
    else
    {

    ?>
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">
      <!-- MAX_FILE_SIZE set to 5 MB -->
      <input type="hidden" name="MAX_FILE_SIZE" value="5242880" />
      <input name="my_file1" type="file" size="30" /><br />
      <input name="my_file2" type="file" size="30" /><br />
      <input name="my_file3" type="file" size="30" /><br />
      <input name="submit" type="submit" value="Upload Files" />
    </form>
    <?php

    }

    ?>
    </body>
    </html>

  2. #2
    SitePoint Guru
    Join Date
    Dec 2003
    Location
    oz
    Posts
    819
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Looks pretty good!
    Tho, don't put html in ur file upload class - such as you've done in GetErrorMsg()
    Very nice interface btw!

    The only other thing - errors or exceptions.
    PHP5 is out - finally!
    How about using exceptions instead of errors from now on. Tho, sorry for bringing this up in your thread since discussions on this may dwarf your post. Discussion on this point should probably go in another thread.

    Eli

  3. #3
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Great design btw, though can I add something ?

    You may want to think about returning an array of messages, dependent of a file upload being a success or not ?

    That way, you can simply use a foreach() to output the messages, rather than to refer to each file object ?

  4. #4
    SitePoint Enthusiast
    Join Date
    Oct 2003
    Location
    staten island
    Posts
    29
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Any particular reason you did not like PEAR:HTTP_Upload ?
    http://www.zaslavskiy.net
    felix[at]bebinary.com

  5. #5
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by felixz
    Any particular reason you did not like PEAR:HTTP_Upload ?
    Two reasons:
    1. PEAR may not be installed on all systems
    2. I didn't even see that

  6. #6
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lazy_yogi
    Looks pretty good!
    Tho, don't put html in ur file upload class - such as you've done in GetErrorMsg()
    Very nice interface btw!
    Were you thinking along the lines of a separate error class?

    Quote Originally Posted by lazy_yogi
    The only other thing - errors or exceptions.
    PHP5 is out - finally!
    How about using exceptions instead of errors from now on. Tho, sorry for bringing this up in your thread since discussions on this may dwarf your post. Discussion on this point should probably go in another thread.
    I can't use any PHP 5 OO implementations due to the need for backward compatibility. Good suggestion, though.

  7. #7
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Widow Maker
    Great design btw, though can I add something ?

    You may want to think about returning an array of messages, dependent of a file upload being a success or not ?

    That way, you can simply use a foreach() to output the messages, rather than to refer to each file object ?
    Thanks, that's a good idea. I'm going to implement that.

  8. #8
    SitePoint Member
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What a great job! finally an example that works.....thanks

    I'm new to PHP and this is for me a great starting point.

  9. #9
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey, I'm glad you're finding it useful. I've got some more work to do on the class(es) when I get some spare time.

  10. #10
    SitePoint Member
    Join Date
    Apr 2004
    Location
    Canada
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I got a suggestion, since the class purpose is mostly but not stick to, to upload images, what about a resize function!

  11. #11
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the suggestion. I'll keep that in mind for the next release. One thing that's being added for sure is better checking of file types using the file's MIME type.

  12. #12
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Mailman
    I got a suggestion, since the class purpose is mostly but not stick to, to upload images, what about a resize function!
    Better yet, just extend that class to ImageFileUpload and add the method in there

  13. #13
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nope, I wouldn't extend this class just to basically validate a filename extension, whereas this task is better suited to this class method, below

    PHP Code:
    ...
    function & 
    AddFile($fileArray
        { 
            
    $fd = new FileDesc

            
    $fd->SetFromFileArray($fileArray); 
            return (
    $this->mFiles[] = $fd); 
        }
    ... 
    As validating a file extension isn't really complex and doesn't warant it's own class, although for the point on image resizing, you would extend the class, as you may at a later date, find you want other useful features, such as borders, for example ?

  14. #14
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Widow Maker
    Nope, I wouldn't extend this class just to basically validate a filename extension, whereas this task is better suited to this class method, below
    Tanus meant to extend the class to provide for special image manipulation functions on uploaded images.

  15. #15
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Tanus meant to extend the class to provide for special image manipulation functions on uploaded images.
    Sorry Tanus That's exactly what I've suggested also apparantly

  16. #16
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I am horribly offended and will now go cry in the corner.


  17. #17
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)


    No offence intended

  18. #18
    SitePoint Wizard Dean C's Avatar
    Join Date
    Mar 2003
    Location
    England, UK
    Posts
    2,906
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey this is v.nice - I started writing something similar a while ago - good job !

    I'm new to OOP so excuse me if this is not a good way of doing it but why not pass the upload directory to the constructor along with an array of allowed filetypes etc. Just makes it easier to remember what to do

    i.e. new lala('C://.....', array('gif', 'jpe'));

  19. #19
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Mist
    I'm new to OOP so excuse me if this is not a good way of doing it but why not pass the upload directory to the constructor along with an array of allowed filetypes etc. Just makes it easier to remember what to do
    I suppose you could do that, but I'm a fan of making the constructor do hardly any "real" work.

  20. #20
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A good compromise is to make them optional arguments. That way people who do like passing simple args can, and those who like lazy constructors don't need to.

  21. #21
    SitePoint Enthusiast therat's Avatar
    Join Date
    May 2004
    Location
    London
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by mattjacob
    Thanks for the suggestion. I'll keep that in mind for the next release. One thing that's being added for sure is better checking of file types using the file's MIME type.
    Just found this script and it works perfectly. Have you been able to make the changes and released a newer version yet. I really need the image resize bits aswell.

  22. #22
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by therat
    Just found this script and it works perfectly. Have you been able to make the changes and released a newer version yet. I really need the image resize bits aswell.
    Nope, nothing yet. I don't have time right now, but you can extend the class yourself to handle image resizing.

  23. #23
    SitePoint Enthusiast therat's Avatar
    Join Date
    May 2004
    Location
    London
    Posts
    46
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks, i'll give it a go

  24. #24
    SitePoint Wizard
    Join Date
    Oct 2001
    Location
    Tucson, Arizona
    Posts
    1,858
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I recently had to write some image resizing code for a separate project, so here's the result of that effort. It'll probably get worked into my file upload project at some point in the future.
    PHP Code:
    function resize_image($src_img_path$dest_img_path$resize_val$resize_mode '%'$dest_img_qual 75)
    {
        
    // Function: resize_image
        // Resizes a JPEG and outputs it to browser or file
        // Author: Matt Jacob <matt.jacob@gmail.com>
        // Last updated: 2004-07-17
        
        // To output the new image to the browser, set $dest_img_path to '' and 
        // send a header of 'Content-Type: image/jpeg'

        // If $dest_img_path is not set to '', the new image will be saved to
        // $dest_img_path
        
        // Possible values for $resize_mode: 
        // - 'x' (width)
        // - 'y' (height)
        // - '%' (percentage)

        // $dest_img_qual is the quality of the new JPEG and it can be any 
        // number between 1 and 100

        
    $src_img['src'] = imagecreatefromjpeg($src_img_path);
        
    $src_img['x'] = imagesx($src_img['src']);
        
    $src_img['y'] = imagesy($src_img['src']);

        switch (
    $resize_mode)
        {
            case 
    'x':
                
    $dest_img['x'] = $resize_val;
                
    $dest_img['y'] = round(($dest_img['x'] / $src_img['x']) * $src_img['y']);

                break;

            case 
    'y':
                
    $dest_img['y'] = $resize_val;
                
    $dest_img['x'] = round(($dest_img['y'] / $src_img['y']) * $src_img['x']);

                break;

            case 
    '%':
                if (
    $resize_val 1)
                {
                    
    $resize_val $resize_val 100;
                }

                
    $dest_img['x'] = round($resize_val $src_img['x']);
                
    $dest_img['y'] = round($resize_val $src_img['y']);

                break;
        }

        
    $dest_img['src'] = imagecreatetruecolor($dest_img['x'], $dest_img['y']);
        
    imagecopyresampled($dest_img['src'], $src_img['src'], 0000$dest_img['x'], $dest_img['y'], $src_img['x'], $src_img['y']);
        
    imagejpeg($dest_img['src'], $dest_img_path$dest_img_qual);

        
    imagedestroy($src_img['src']);
        
    imagedestroy($dest_img['src']);


  25. #25
    SitePoint Guru OfficeOfTheLaw's Avatar
    Join Date
    Apr 2004
    Location
    Quincy
    Posts
    636
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Here's mine

    I was bored last night, and started playing around with php5's new features by making my own file upload class. Please let me know what is missing, and what could be added!

    I'm considering adding on the option of storing it's location in a database, but since that isn't a feature of a generic, barebones functionality, that will probably be a seperate class that extends this one.

    PHP Code:

    /* Error global messages */
    define(ERR_NOT_WRITABLE'Could not upload file. Make sure directory is writable by script');
    define(ERR_DOESNT_EXIST'Directory/File does not exist.');
    define(ERR_FILE_TYPE'Invalid filetype: must be gif, jpeg, or png');
    /* end global err msgs */

    /*
     * @name is a string that may have naughty chars in it
     */
    function clean_string($name)
       {
       return 
    preg_replace('/[^a-z0-9_\-\.]/i''_',$name);
       }
    /* takes an arguement of $_FILES array, iterates through any files
       and stores them in the database.
     */

    class uploadedFile
       
    {
       
    /*
        * @files = array of uploaded files
        */
       
    private $files = array();
       private 
    $types '';
       
       public function 
    __construct(&$files$types=NULL)
          {
          
    $this->files = &$files;
          
    $this->checkErrorStatus();
          
          if(
    $types != NULL)
             
    $this->setTypes($types);
          else
             
    $this->types '/.*/';
          }

       private function 
    checkErrorStatus()
          {
          foreach(
    $this->files as $key => $file)
                {
                try
                   {
                   switch(
    $file['error'])
                      {
                      case 
    UPLOAD_ERR_INI_SIZE:
                         throw new 
    Exception('File exceeds maximum upload size');
                         break;
                      case 
    UPLOAD_ERR_FORM_SIZE:
                         throw new 
    Exception(ERR_NOT_WRITABLE);
                         break;
                      case 
    UPLOAD_ERR_PARTIAL:
                         throw new 
    Exception('The file ' $file['name'] . ' was not completely uploaded');
                         break;
                      case 
    UPLOAD_ERR_NO_FILE:
                         unset(
    $this->files[$key]);    /* no file uploaded, we can remove it */
                         
    break;
                      }
                   }
                catch(
    Exception $e)
                   {
                   echo 
    $e->getMessage();
                   }
                }
          }
       
    /*
        * function: sets $this->types as a regex of 
        * acceptable mimetypes.
        *
        * @types = array of acceptable data types
        */
       
    private function setTypes($types)
          {
          
          foreach(
    $types as $i => $string)
             {
             
    $this->types .= '('.str_replace('/','\/',$string).')';
             if(
    $i count($types))
                
    $this->types .= '|';
             }
          
    /* make regex */
          
    $this->types '/'.$this->types.'/';
          }
       
       public function 
    store($filedir)
          {
          try
             {
             foreach(
    $this->files as $key=>$file)
                {
                if(!
    file_exists($filedir))
                   throw new 
    Exception(ERR_DOESNT_EXIST);
                elseif(!
    is_writeable($filedir))
                   throw new 
    Exception(ERR_NOT_WRITABLE);
                elseif (!
    preg_match($this->types$file['type']))
                   throw new 
    Exception(ERR_FILE_TYPE);
                else
                   
    move_uploaded_file($file['tmp_name'], $filedir clean_string($file['name']));
                
                }
             }
          catch (
    Exception $e)
             {
             echo 
    $e->getMessage();
             }
          }
       public function 
    dump()
          {
          
    var_dump($this->files);
          }
       
       }
    $imagedir '/var/www/zendcomp/images/';
    $types = array('image/gif','image/png','image/jpeg');

    $filehandler = new uploadedFile($_FILES$types);
    $filehandler->store($imagedir);
    $filehandler->dump(); 
    EDIT: Before anyone asks how come clean_string is a function outside of the class, I created it at the time to allow flexibility, then realized I may use the function on other parts of my application.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •