Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php]Początki OOP
Forum PHP.pl > Forum > PHP > Object-oriented programming
Mefiuu
Witam. Rozpocząłem żmudne zagłębianie się w tajniki programowania zorientowanego obiektowo, przeczytałem kilka artykułów i pomyślałem że najlepiej jest sprawdzić swoją wiedzę w praktyce. Tak oto powstała klasa do uploadu plików. Przyznam się, że pobrałem sobie podobną klasę z phpclasses.org, aby zobaczyć mniej więcej jak to wygląda. Chciałbym Was zapytać czy ma to coś w ogóle wspólnego ze stylem OOP ?

class.upload.php
  1. <?php
  2. class Upload {
  3. var $folder;
  4. var $types = array('application/msword', 'application/octet-stream', 'image/jpeg');
  5.  
  6. function __construct($folder) {
  7. $this->folder = $folder;
  8. $this->upload();
  9. }
  10.  
  11. public function check() {
  12. if (isset($_POST['wyslij'])) {
  13. if($_FILES['plik']['error']>0) {
  14. echo "Wystąpił problem: ";
  15. switch($_FILES['plik']['error']) {
  16. case 1: echo "rozmiar pliku przekroczył wartość xMB.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;
  17. case 2: echo "rozmiar pliku przekroczył wartość xMB.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;
  18. case 3: echo "plik wysłany częściowo.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;
  19. case 4: echo "nie wysłano żadnego pliku.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;
  20. case 6: echo "nie można wysłać pliku: nie wskazano katalogu tymczasowego.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;
  21. case 7: echo "nie zapisano pliku na dysku.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;
  22. }
  23. }
  24. else {
  25. if (in_array($_FILES['plik']['type'], $this->types)) {
  26. return true;
  27. }
  28. }
  29. }
  30. }
  31.  
  32. public function upload() {
  33. if($this->check()) {
  34. if (is_uploaded_file($_FILES['plik']['tmp_name'])) {
  35. if(!file_exists($this->folder.'/'.$_FILES['plik']['name'])) {
  36. if(move_uploaded_file($_FILES['plik']['tmp_name'], $this->folder.'/'.$_FILES['plik']['name'])) {
  37. throw new Exception('Dodano plik pomyślnie');
  38. }
  39. else {
  40. throw new Exception('Nie dodano pliku!');
  41. }
  42. }
  43. else {
  44. throw new Exception('Taki plik już istnieje!');
  45. }
  46. }
  47. }
  48. else {
  49. throw new Exception('Niepoprawny typ!');
  50. }
  51. }
  52. }
  53. ?>



index.php
  1. <?php
  2.  
  3. include('class.upload.php');
  4.  
  5. try {
  6. $upload = new Upload('files');
  7. }
  8. catch (Exception $e) {
  9. echo $e->getMessage();
  10. }
  11.  
  12. ?>



Mam co do tego pytania :

1) Wiem że w stylu OOP są wyjątki. Czy to, jak ja je zastosowałem to odpowiednia metoda? Czy jest ich za dużo ? Bo mi nie pasują za bardzo ...
2) Czy wyjątki powinno się dawać do udanych operacji ?
3) Czy w konstruktorze wykonywać metodę 'upload' czy odwołać się do niej poza konstruktorem, już w pliku index?
4) Jeśli mam operacje na bazie danych bądź plikach tekstowych to czy tworzyć destruktor do ich zamykania ?

To takie ogólne pytania i byłbym bardzo wdzięczny gdyby ktoś mi to 'sprawdził', wytknął błędy i polecił dobrą literaturę smile.gif

Dziękuję i pozdrawiam.
konole
Tak na początek: najlepiej, jeśli klasa nie zwraca żadnego tekstu - to najlepiej wyłapywać w trakcie programu.
Mefiuu
czyli rozumiem że wyłapywać w index? Ale nie bardzo rozumiem jak można to tam przechwycić
Spawnm
$_FILES['plik']['error'] - i już wiem że twoja klasa nie pomoże przy przesłaniu 2 plików lub pliku $_FILES['avatar'].
Echo w klasie uploadu ?!
Kod
throw new Exception('Dodano plik pomyślnie');

Jeśli coś się udało czemu zgłaszasz wyjątek?

Kod
case 2: echo "rozmiar pliku przekroczył wartość xMB.<br /><a href='java script:history.back(1)'>Wróć</a>"; break;

html w klasie upload?!
Crozin
Na początek dwie ogólne uwagi:
1. Nie mieszaj języków (patrz: wywal polskie indeksy tablic). Angielski jest jedynym słusznym w takim miejscu. wink.gif
2. Nigdy nie doprowadzaj do czegoś takiego:
  1. if () {
  2. if () {
  3. if () {
  4. if () {
  5.  
  6. } else {
  7.  
  8. }
  9. } else {
  10.  
  11. }
  12. } else {
  13.  
  14. }
  15. } else {
  16.  
  17. }
Tyle poziomów zagłębień to koszmar przy późniejszej pracy. Niemal zawsze da się to zamienić na coś w stylu:
  1. // Obsługa czterech "błędów"
  2. if (!...) {
  3.  
  4. }
  5.  
  6. if (!...) {
  7.  
  8. }
  9.  
  10. if (!...) {
  11.  
  12. }
  13.  
  14. if (!...) {
  15.  
  16. }
  17.  
  18. // Obsługa po pomyślnym przejściu przez powyższe
  19. ...
Pamiętaj, że wyrzucenie wyjątku przerywa wykonywanie bloku TRY.

Co do Twoich pytań:
1. Nie jest ich za dużo. W każdym miejscu gdzie występuje błąd, który wystąpić nie powinien masz prawo, a wręcz obowiązek skorzystać z wyjątku.
2. Nie.
3. Nie powinieneś. Konstruktor służy przygotowaniu obiektu do pracy, nie wykonywaniu samej pracy.
4. To zależy. Jeżeli plik jest otwarty na przestrzeni "działania obiektu" to w destruktorze dobrze byłoby upewnić się, że plik zostanie zamknięty. Przy czym raczej powinna być osobna metoda do zamknięcia, użytkownik powinien zawsze ręcznie ją wywoływać, a destrukor mógłby co najwyżej w ostateczności zamykać. Coś w ten deseń:
  1. class File {
  2. private $opened = false;
  3.  
  4. public function open(...) {
  5. ...
  6.  
  7. $this->opened = true;
  8. }
  9.  
  10. public function write(...) {
  11. ...
  12. }
  13.  
  14. public function close() {
  15. ...
  16.  
  17. $this->opened = false;
  18. }
  19.  
  20. public function __descruct() {
  21. if ($this->opened) {
  22. $this->close();
  23. }
  24. }
  25. }


I jeszcze kilka uwag:
1. Nazwa klasy powinna być rzeczownikiem, np. Uploader. Tworząc obiekt (new) tworzysz jakiś byt, a następnie każesz owemu bytowi wykonywać jakieś akcje, np. wgrać coś ($uploader->uploadSomething()) dlatego też nazwy metod powinny być czasownikami. Kod jest wtedy bardziej logiczny.
2. W PHP5 nie ma czegoś takiego jak "var". Masz trzy modyfikatory zasięgu: private, protected, public i to z nich powinieneś korzystać.
3. Nigdy nie powinieneś korzystać ze stanu globalnego (patrz $_POST, $_FILES). Te dane powinny zostać przekazane do obiektu z zewnątrz.
4. Akurat na kod błędu (1..7) masz ładne stałe, które są bardziej wymowne niż jakiś numerek.
5. Nigdy nie powinieneś mieć w kodzie takiej klasy żadnego ECHO. Jest błąd? To wywalasz wyjątek. Jakaś inna część aplikacji może go przechwycić i zrobić coś pożytecznego z nim (jak chcociażby wyświetlenie informacji użytkownikowi).
6. Nigdy nie stosuj bezpośrednio klasy Exception. Jest zbyt ogólna. Użyj bardziej wyspecjalizowanego typu. A jeżeli w SPL-u nie ma odpowiedniego, stwórz własny.
7. Jak już przechwytujesz wyjątek (blok CATCH) zrób z nim coś pożytecznego. Wyświetlenie treści wyjątku nie jest czymś takim. Co najwyżej całą aplikację możesz objąć blokiem TRY .. CATCH i tutaj wyświetlenie treści błędu ma sens - bo oznacza to, że aplikacja się wysypała i nic już nie da się zrobić.
Mefiuu
Bardzo dziękuję za odpowiedzi i pomoc ! Crozin, Twoje uwagi okazały się bardzo pomocne. Zmieniłem nieco mój kod i obecnie wygląda on tak:

class.uploader.php
  1. <?php
  2. class Uploader {
  3. public $folder;
  4. public $types = array('application/msword', 'application/octet-stream', 'image/jpeg');
  5. public $file;
  6.  
  7.  
  8. function __construct($file, $folder) {
  9. $this->folder = $folder;
  10. $this->file = $file;
  11. }
  12.  
  13. public function checkType() {
  14. if($_FILES[$this->file]['error']>0) {
  15. switch($_FILES[$this->file]['error']) {
  16. case 1: throw new Exception ("rozmiar pliku przekroczył wartość xMB."); break;
  17. case 2: throw new Exception ("rozmiar pliku przekroczył wartość xMB."); break;
  18. case 3: throw new Exception ("plik wysłany częściowo."); break;
  19. case 4: throw new Exception ("nie wysłano żadnego pliku."); break;
  20. case 6: throw new Exception ("nie można wysłać pliku: nie wskazano katalogu tymczasowego."); break;
  21. case 7: throw new Exception ("nie zapisano pliku na dysku."); break;
  22. }
  23. }
  24. if (!in_array($_FILES[$this->file]['type'], $this->types)) {
  25. return false;
  26. }
  27. else {
  28. return true;
  29. }
  30. }
  31.  
  32. public function uploadFile() {
  33. if(!$this->checkType()) {
  34. throw new Exception('Niepoprawny typ pliku!');
  35. }
  36.  
  37. if (!is_uploaded_file($_FILES[$this->file]['tmp_name'])) {
  38. throw new Exception('Nie wysłano pliku!');
  39. }
  40.  
  41. if(file_exists($this->folder.'/'.$_FILES[$this->file]['name'])) {
  42. throw new Exception('Taki plik już istnieje');
  43. }
  44.  
  45. if(!move_uploaded_file($_FILES[$this->file]['tmp_name'], $this->folder.'/'.$_FILES[$this->file]['name'])) {
  46. throw new Exception('Nie dodano pliku');
  47. }
  48.  
  49. return true;
  50. }
  51. }
  52. ?>


index.php
  1. <?php
  2.  
  3. include('class.uploader.php');
  4.  
  5. try {
  6. $upload = new Uploader('file','files');
  7. if ($upload->uploadFile()) {
  8. echo 'Udało się wysłać plik!';
  9. }
  10. }
  11. catch (Exception $e) {
  12. echo $e->getMessage();
  13. }
  14.  
  15. ?>



Mam teraz kolejne pytanie, czy poprawnie został użyty kod :

  1. if ($upload->uploadFile()) {
  2. echo 'Udało się wysłać plik!';
  3. }


?

Jednak dalej nie wiem, jak zmienić ten główny blok try-catch. Ponadto, jak Crozin wspomniałeś, nie powinienem stosować tablic globalnych, tylko je przekazywać do obiektu. W jaki sposób (przykładowo) mogę przekazać do obiektu tablicę $_FILES?

@Spawnm:
Nie wiem czy o to Ci chodziło, ale teraz do konstruktora przekazuję nazwę $_FILES i jeśli jest to $_FILES['avatar'] to wystarczy że to do konstruktora przekażę. Czy źle zrozumiałem?
Ponadto pozbyłem się wyjątku w udanej operacji, oraz kodu html.

Dziękuję jeszcze raz za wszelką pomoc i proszę o kolejne uwagi.

Pozdrawiam.


P.S. Spawnm to Twoje 400 'Pomógł'. Gratulacje wink.gif
Crozin
Cytat
Mam teraz kolejne pytanie, czy poprawnie został użyty kod :
Tak. Generalnie o to właśnie chodzi.

Jednak nadal pewne rzeczy są zrobione źle / w nienajlepszy sposób:
1. Zacznij korzystać z autoloaderów (jest wiele dobrych, gotowych rozwiązań).
2. Przykładaj większą uwagę do nazewnictwa. Nie ma to wpływu na działanie programu, ale zdecydowanie ułatwia korzystanie z niego. Zmienna $upload powinna raczej nazywać się $uploader w tym przypadku, $file (nazwa indeksu z tablicy $_FILES) jest dosyć myląca. W takim kontekście sugeruje ona raczej nazwę wgrywanego pliku czy coś takiego.
3. Jeżeli metoda checkType() jest wyłącznie do użytku wewnętrznego powinieneś ją oznaczyć jako niepubliczną.
4. Jak już wspomniałem: http://www.php.net/manual/en/features.file-upload.errors.php - to nieco oczyści kod.
5. Dlaczego metoda checkType(), której nazwa dosyć jasno wskazuje na to, że metoda sprawdza czy typ mime pliku jest odpowiedni zajmuje się jakimiś pierdołami w stylu sprawdzania błędów z $_FILES? To zadanie dla innej metody.
6.
  1. if (!in_array($_FILES[$this->file]['type'], $this->types)) {
  2. return false;
  3. }
  4. else {
  5. return true;
  6. }
  7.  
  8. // VS
  9.  
  10. return !n_array($_FILES[$this->file]['type'], $this->types);
Komentarz chyba zbędny?
7. Nadal używasz bezpośrednio klasy Exception. Nie powinieneś tego robić.
8. $_FILES['...']['type'] nie jest wiarygodnym źródłem danych - może być dowolnie modyfikowane. W tej chwili nie ma problemu by wgrać przykładowo plik PHP przez ten skrypt.

Cytat
Jednak dalej nie wiem, jak zmienić ten główny blok try-catch.
Przykład ilustrujący o co chodziło:
  1. <?php
  2.  
  3. try {
  4. // cała strona (cały system) tutaj
  5.  
  6. try {
  7. $uploader = new Uploader( ... );
  8. if ( ... ) {
  9. ..
  10. }
  11. } catch (FileUploadException $fue) {
  12. $this->showMessage($fue->getMessage());
  13. } catch (IOException $ioe) {
  14. // jakaś obsługa błędu po stronie serwera
  15. // albo jak się nic nie da sensownego zrobić:
  16. throw $ioe;
  17. }
  18.  
  19. // cała strona (cały system) tutaj
  20. } catch (Exception $e) {
  21. die('Coś się posypało, błąd krytyczny!');
  22. }


Cytat
Ponadto, jak Crozin wspomniałeś, nie powinienem stosować tablic globalnych, tylko je przekazywać do obiektu. W jaki sposób (przykładowo) mogę przekazać do obiektu tablicę $_FILES?
Tak jak masz obecnie nie jest najgorzej. Wątpię byś musiał się na chwilę obecną zamartwiać nad problemem uploadu pliku, który nie jest obsługiwany przez $_FILES, więc możesz zostawić jak jest.
Mefiuu
Dziękuję, chyba jak na razie poprawiłem znacząco mój kod i wiem mniej więcej czym się kierować w dalszej nauce. Z Twoich wyliczeń zrobiłem prawie wszystko (oprócz punktu 1 i 8 ale o tym jeszcze doczytam / przemyślę). Dziękuję serdecznie za pomoc.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.