PHP folosind OOP - Uploading files

Post Title

Salut tuturor, in acest articol vom pune in practica un exemplu clar prin care invata mai multe despre stilul de programare orientat pe obiecte.In articolul anterior am invatat despre cum putem incarca fisiere pe server si am vazut ca este destul de mult cod, foarte multe verificari si asa mai departe.Ei bine, de asemenea, folosind OOP vom scrie foarte mult cod, ce-i drept, o singura data, insa vom organiza totul mult mai bine, intr-o singura entitate.

Stim ca folosind OOP, putem grupa intr-o singura entitate datele si functiile care lucreaza cu aceste date.Ei bine, hai sa vedem care sunt aceste date.In primul rand, variabila cea mai importanta este $_FILES deoarce contine toate informatiile despre fisierul incarcat, deci, cu alte cuvinte, am descoperit cel mai important element.In continuare, am vazut ca am folosit o variabila ce contine toate extensiile permise in procesul de upload, acea variabila fiind $allowedExtensions.Mai deaprte, stim ca am avut o marime maxima a fisierului ( 500 KB din cate imi amintesc ), iata am mai descoperit inca o variabila importanta, sa o denumim $maximumFileSize.Desi nu am folosit aceasta functionalitate in articolul precedent, este posibil ca voi sa doriti schimbarea numelui fisierului, ceea ce inseamna ca avem nevoie de o variabila pentru acest nume, sa zicem $fileName.De asemenea, am vazut ca avem nevoie de locatia undeva vom muta fisierul din locatia temporara, sa zicem ca vom stoca aceasta locatie in variabila $uploadDirectory.Nu in ultimul rand, stim ca in procesul de upload vom avea erori, deci avem nevoie de o variabila pentru stocarea acestor erori, sa zicem $errors.

Ei bine, dragi cititori, ce amuzant suna, iata am descoperit datele necesare, haide sa grupam aceste date in clasa ce o vom crea.

<?php

class FileUpload
{
    /**
     * @var Propietatea ce contine toate datele despre fisier
     */
    protected $file = [];

    /**
     * @var Propietatea ce contine noul nume al fisierului
     */
    protected $fileName = '';

    /**
     * @var Propietatea ce contine toate extensiile permise
     */
    protected $allowedExtensions = [];

    /**
     * @var Propietatea ce contine marimea maxima a fisierului
     */
    protected $maximumSize = 0;

    /**
     * @var Propietatea ce contine locatia unde vom stoa fisierul
     */
    protected $uploadDirectory = '';

    /**
     * @var Propietatea ce contine toate erorile din procesul de upload
     */
    protected $errors = [];

}

De notat este faptul ca aceste variabile, folosind OOP, se numesc proprietati.

Pana acum, totul este bine, probabil va intrebati ce este cu acel protected in fata tuturor proprietatilor. Ei bine, folosind OOP putem seta o vizibilitate pentru aceste date, astfel incat sa modificam mediul ( in exteriorul clasei, in interiorul clasei, intr-o clasa copil ) unde aceste proprietati pot fi accesate.Am ales protected deoarece nu vrem ca datele sa fie accesate in exteriorul clasei, insa pot fi accesate in interioul unei clase copil, astfel profitam de unul dintre avantajele folosirii acestui stil de programare, cu alte cuvinte, ne folosim de ENCAPSULATION.

ENCAPSULATION presupune ascunderea datelor ( ceea ce am si facut ) oferind mijloace pentru accesarea si modificarea acestor date ( folosind functii pentru acest lucru ).

De ce ascundem aceste date? Ei bine, nu dorim ca prin accesarea lor directa sa stricam totul ( vom mai discuta noi in alte articole ), trebuie sa ne asiguram ca aceste date vor fi modificate asa cum dorim noi sa fie modificate, ceea ce inseamna ca folosind functiile oferim aceste mijloace de modificare.

Am spus ca vom folosi functii pentru modificarea acestor date, deja am invatat elementele principale in procesul de crearea al unei clase, proprietatile ( datele ) si functiile care lucreaza cu aceste date, fiind numite, metode atunci cand lucram cu OOP.

Sa mergem mai departe, am spus ca elementul principal clasei noastre este proprietatea $file, aceasta propietate va contine toate informatiile despre fisierul incarcat.Ei bine, putem spune ca aceasta clasa este depedenenta ( vom mai disuta despre aceasta relatie de dependenta ) de aceasta proprietate pentru a functiona.

Codul de mai jos face parte din structura clasei, sa incepem prin setarea depedentei, mereu vom face acest lucru inainte de orice.

Ei bine, aceasta este o metoda, dar este o metoda mai speciala, in OOP astfel de metode se numesc functii magice.Aceasta functia va fi apelata automat atunci cand vom crea un obiect din aceasta clasa ( asa functioneaza ea ).Cu alte cuvinte, atunci cand vom face $upload = new FileUpload($_FILES['image']);, dupa cum puteti observa parametrul metodei va avea ca valoare elementul principal.Aceasta este forma de declararea a acestei metode.De retinut este faptul ca aceasta functie magica se foloseste atunci cand setam depedentele clasei si se scrie inainte de orice alte metode, asa cum am spus, inainte de orice setam depedentele clasei.

De notat este faptul ca si metodele au o anumita vizivilitate pe care noi o putem seta, `public inseamna ca functia/propietatea poate fi accesata in exteriorul clasei atunci cand cream un nou obiect.Ei bine, aceasta metoda fiind folosita pentru setarea depedentei clasei, trebuie sa poata fi accesata in exterior.

public function __construct($file = []) 
{
    $this->file = $file;
}

In continuare, adaugam inca doua metode, prima metoda fiind folosita pentru a returna numele fisierelui, urmatorea metoda fiind folosita pentru a seta un nou nume pentru fisier.Asa cum am spus, metodele sunt folosite pentru a lucra cu aceste date, ceea ce noi chiar facem.Probabil va intrebati ce face $this->.Ei bine, aceasta este o pseudo variabila si este folosita pentru a accesa propietatile si metodele din interiorul unei clase, ea reprezinta o referinta catre obiectul ce va fi creat.Dupa cum puteti observa, am accesat propietatea $this->file['name'];.De asemenea, folosind aceasta pseduo varibila puteti modifica valoarea unei propietati in interiorul clasei.In exteriorul clasei, atunci cand cream un obiect din aceasta clasa, se foloseste obiectul insusi $object->.

Ambele metode au vizibilitatea public, avem nevoie de ele in exterior.

public function getFileName()
{
    return $this->file['name'];
}

public function setFileName($fileName = '')
{
    $this->fileName = $fileName;
}

In continuare, mai adaugam niste metode prin care sa putem lucra cu datele noastre.

public function getExtension()
{
    return pathInfo($this->getFileName(), PATHINFO_EXTENSION);
}

public function setAllowedExtensions($ext = [])
{
    $this->allowedExtensions = $ext;
}

public function getTmpLocation()
{
    return $this->file['tmp_name'];
}

public function getFileSize()
{
    return $this->file['size'];
}

public function setMaximumFileSize($maxSize)
{
    $this->maximumSize = $maxSize;
}

public function setUploadDirectory($path)
{
    this->uploadDirectory = $path;
}

Numele acestor metode spune chiar ceea ce fac ele, se intelege de la sine.

In continuare, dupa cum am vazut in articolul precedent, am facut foarte multe verificari, ei bine, vom muta aceste verificari in interiorul clasei, in metode, insa vom avea mici modificari.Dupa cum puteti vedea, aceste metode au vizibilitatea protected, ei bine, aceste metode vor fi folosite in interior.De asemenea, puteti observa ca erorile sunt stocate in proprietatea $errors, asa cum am spus.

protected function checkUploadErrors()
{
    if ($this->file['error'] > 0) {
        $this->errors[] = 'The file could not be uploaded, please try again!';
    }
}

protected function checkExtension()
{
    if (!in_array($this->getExtension(), $this->allowedExtensions)) {
        $this->errors[] = 'The file format is not compatible';
    }
}

protected function checkFileSize()
{
    if ($this->getFileSize() > $this->maximumSize) {
        $this->errors[] = 'The uploaded file is to big!';
    }
}

In continuare, mai trebuie sa adaugam inca o metoda similara cu cele de sus, insa este putin mai complicata.Trebuie sa verificam ce nume vom folosi pentru fisierul de urmeaza a fi stocat pe server.In functie de rezultatul functiei move_uploaded_files() se returneaza o valoarea booleana.Avem nevoie de aceasta valoare in urmatoarea metoda.

protected function checkIfUploaded()
{
    $fileName = $this->fileName . '.' . $this->getExtension();

    if (empty($this->fileName)) {
        $fileName = $this->getFileName();
    }

    $path = $this->uploadDirectory . $fileName;

    if (!move_uploaded_file($this->getTmpLocation(), $path)) {
        $this->errors[] = 'The file could not be uploaded, please try again!';

        return false;
    }

    return true;
}

Metoda de mai jos porneste tot procesul de upload, se fac verificarile necesare si se incerca stocarea fisierului pe server. Aceasta metoda are vizibilitatea public fiind folosita in exterior, veti intelege despre ce vorbesc in curand.

public function isUploaded()
{
   // apelam toate functiile necesare verificarii procesului de upload.
    $this->checkUploadErrors();
    $this->checkExtension();
    $this->checkFileSize();

    // daca inca nu sunt erori, incercam sa stocam fisierul pe server.
    // stim ca metoda checkIfUploaded() va intoarce o valoare booleana, 
   // de asemenea, valoarea booleana va fi intoarsa si de aceasta functie.
    if (!count($this->getErrors())) {
        return $this->checkIfUploaded();
    }

    return false;
}

Mai departe, avem nevoie de doua metode pentru preluarea erorile, prima metoda returneaza toate erorile, fiind stocate intr-o matrice pe mai multe dimensiuni.Urmatoarea metoda returneaza prima eroare din lista cu erori.

public function getErrors()
{
    return $this->errors;
}

public function getFirstError()
{
    if (isset($this->getErrors()[0])) {
         return $this->getErrors()[0];
    }
}

Aceste este tot codul din fisierul UploadFile.php fisier ce contine clasa de mai sus.

UploadFile.php

<?php

class FileUpload
{
    /**
     * @var Variabila ce contine toate datele despre fisier
     */
    protected $file = [];

    /**
     * @var Variabila ce contine noul nume al fisierului
     */
    protected $fileName = '';

    /**
     * @var Variabila ce contine toate extensiile permise
     */
    protected $allowedExtensions = [];

    /**
     * @var Variabila ce contine marimea maxima a fisierului
     */
    protected $maximumSize = 0;

    /**
     * @var Variabila ce contine locatia unde vom stoa fisierul
     */
    protected $uploadDirectory = '';

    /**
     * @var Variabila ce contine toate erorile din proceul de upload
     */
    protected $errors = [];

    /**
     * Metoda ce seteaza depdentele clasei
     * @param array $file
     */
    public function __construct($file = [])
    {
        $this->file = $file;
    }

    /**
     * Metoda folosita pentru preluarea numelui fisierului
     * @return string
     */
    public function getFileName()
    {
        return $this->file['name'];
    }

    /**
     * Metoda folosita pentru a seta un nou nume pentru fisier
     * @param string $fileName
     */
    public function setFileName($fileName = '')
    {
        $this->fileName = $fileName;
    }

    /**
     * Metoda pentru preluarea extensiei fisierului
     * @return string
     */
    public function getExtension()
    {
        return pathInfo($this->getFileName(), PATHINFO_EXTENSION);
    }

    /**
     * Metoda pentru setarea unei liste cu toate extensiile permise
     * @parm array $ext
     */
    public function setAllowedExtensions($ext = [])
    {
        $this->allowedExtensions = $ext;
    }

    /**
     * Metoda pentru preluarea locatiei temporare
     * @return string
     */
    public function getTmpLocation()
    {
        return $this->file['tmp_name'];
    }

    /**
     * Metoda pentru preluarea marimii fisierului
     * @return integer
     */
    public function getFileSize()
    {
        return $this->file['size'];
    }

    /**
     * Metoda pentru setarea unei marimii maxime a fisierelor incarcate
     * @param integer $maxSize
     */
    public function setMaximumFileSize($maxSize)
    {
        $this->maximumSize = $maxSize;
    }

    /**
     * Metoda pentru setarea locatiei unde fisierul va fi stocat
     * @param string $path
     */
    public function setUploadDirectory($path)
    {
        $this->uploadDirectory = $path;
    }

    /**
     * Metoda pentru verificarea erorilor din procesul de upload
     */
    protected function checkUploadErrors()
    {
        if ($this->file['error'] > 0) {
            $this->errors[] = 'The file could not be uploaded, please try again!';
        }
    }

    /**
     * Metoda pentru verificarea extensiei fisierelor incarcate
     */
    protected function checkExtension()
    {
        if (!in_array($this->getExtension(), $this->allowedExtensions)) {
            $this->errors[] = 'The file format is not compatible';
        }
    }

    /**
     * Metoda pentru verificarea marimii maxime a fisierelor incarcate
     */
    protected function checkFileSize()
    {
        if ($this->getFileSize() > $this->maximumSize) {
            $this->errors[] = 'The uploaded file is to big!';
        }
    }

    /**
     * Metoda folosita pentru mutarea fisierului in locatia dorita de noi
     * @return bool
     */
    protected function checkIfUploaded()
    {
        $fileName = $this->fileName . '.' . $this->getExtension();

        if (empty($this->fileName)) {
            $fileName = $this->getFileName();
        }

        $path = $this->uploadDirectory . $fileName;

        if (!move_uploaded_file($this->getTmpLocation(), $path)) {
            $this->errors[] = 'The file could not be uploaded, please try again!';

            return false;
        }

        return true;
    }

    /**
     * Metoda folosita pentru inceperea si verificarii procesului de upload
     * @return bool
     */
    public function isUploaded()
    {
        $this->checkUploadErrors();
        $this->checkExtension();
        $this->checkFileSize();

        if (!count($this->getErrors())) {
            return $this->checkIfUploaded();
        }

        return false;
    }

    /**
     * Metoda folosita pentru returnarea tuturor erorilor
     * @return array
     */
    public function getErrors()
    {
        return $this->errors;
    }

    /**
     * Metoda folosita pentru returnarea primei erori din lista cu erori.
     * @return string
     */
    public function getFirstError()
    {
        if (isset($this->getErrors()[0])) {
            return $this->getErrors()[0];
        }
    }
}

Acest fisier se afla in folderul tutorial, alaturi de folderul uploads si de fisierul index.php.

Ei bine, acesta este fisierul index.php folosindu-ne de OOP pentru incarcarea de fisiere pe server.

index.php

<?php
// ne asiguram ca PHP afiseaza toate erorile
error_reporting(E_ALL);
ini_set('display_errors', 1);

session_start();

// includem fisierul FileUpload.php fisier ce contine clasa FileUpload
require_once __DIR__ . '/FileUpload.php';

// verificam daca o cerere folosind metoda POST a fost trimisa catre server
if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    // verificam daca un fisier a fost incarcat folosind formularul
   // aceasta verificare tine de validarea formularelor
    if (!isset($_FILES['image'])) {
        $_SESSION['alert']['danger'] = 'There was no file uploaded';

        header("Location: index.php");
        exit;
    }

    // cream un obiect din clasa FileUploads si ii oferim depedenta principala
    $upload = new FileUpload($_FILES['image']);

    // setam un nou nume pentru imagine
     $upload->setFileName('poza');

    // setam extensiile permise
     $upload->setAllowedExtensions(['jpeg', 'jpg', 'png']);

     // setam marimea maxima a fisierelor incarcate
    $upload->setMaximumFileSize(500);

     // setam locatia unde vom stoaca fisierele incarcate
    $upload->setUploadDirectory(__DIR__ . '/uploads/');

    // verificam daca ceva nu a mers bine in procesul de upload.
    if (!$upload->isUploaded()) {
        $_SESSION['alert']['danger'] = $upload->getFirstError();

        header("Location: index.php");
         exit;
    }

    // totul este bine
    $_SESSION['alert']['success'] = 'Your file was uploaded!';

    header("Location: index.php");
    exit;

}

?>
<!DOCTYPE html>
<html>
<head>
    <title>Upload a file to the server</title>
    <meta charset="UTF-8">
</head>
<body>
    <?php if (isset($_SESSION['alert']['danger'])): ?>
        <p><?php echo $_SESSION['alert']['danger']; ?></p>
    <?php endif; ?>

    <?php if (isset($_SESSION['alert']['success'])): ?>
        <p><?php echo $_SESSION['alert']['success']; ?></p>
    <?php endif; ?>

    <form action="index.php" method="POST" enctype="multipart/form-data">
        <input type="file" name="image">
        <button type="submit">Upload</button>
    </form>
</body>
</html>
<?php

if (isset($_SESSION['alert'])) {
    unset($_SESSION['alert']);
}

Foarte, foarte mult cod si foarte multe explicatii in acest articol, desi totusi, am incercat sa nu exagerez rau de tot cu explicatiile dat fiind faptul ca ceea ce se petrece in acele functii sunt chestii simple.Ei bine, am lasat comentarii in codul de mai sus, sper ca se inteleg tot ceea ce se afla in fisierul index.php.

Inainte de a ne putea folosi de tot ceea ce am scris in clasa FileUpload, trebuie sa cream un obiect din aceasta clasa.Ganditi-va ca aceasta clasa este o schita, insa trebuie sa cream obiecte din ea ca sa putem folosi ceea ce am scris acolo.Am inceput prin crearea obiectului injectand depedenta principala.

// cream un obiect din clasa FileUploads si ii oferim depedenta principala
$upload = new FileUpload($_FILES['image']);

Mai departe, am accesat metodele necesare procesului de upload si in final, am verificat daca totul este bine.Asa cum am spus, in exteriorul clasei, trebuie sa folosim obiectul creat pentru accesarea metodeleor a caror vizibilitate este public.

Sper ca va este de ajutor, stiu, stiu, este atat de urias, nici mie nu-mi vine sa cred.Atentie, acesta este doar un exemplu, eident nu recomand folosirea acestui cod, ci este doar pentru studiu, as fi mers prea departe folosindu-ma si de type hinting si alte chestii putin mai complicate.

Apropo, daca gasiti erori, nu ezitati sa ma contactat si se rezolva.

Autor articol
David: Why do you want to leave me? Why? I'm sorry I'm not real! If you let me, I'll be so real for you!

Articolul anterior


Comentarii

Comentariu adaugat de Bucur
Catalin sti ce scrie in aceste tutoriale de caliate, felicitari...avem ce sa invatam.
go to page top Bucur | 2016-07-17
Comentariu adaugat de marian
Bravo Catalin pentru inca un articol dus la capat. 
go to page top marian | 2016-07-16
Comentariu adaugat de Birkoff
toate lucrurile din clasa de mai sus se puteau face si mai simplu cu o functie la care i se paseaza ori un array cu parametrii ori fiecare parametru separat (upload directory, maxfilezise, etc) iar functia sa returneze true daca totul e ok, sau false + un mesaj daca e o problema... 

dar pentru exemplificare, e ok si cu o clasa.
go to page top Birkoff | 2016-07-16

Exista 3 comentarii   

  • 1
Trebuie sa fii logat sa poti lasa un comentariu Autentificare Inregistrare Logare cu Facebook
top