Học PHP, Phần 3: Xác thực, các đối tượng, các ngoại lệ và tạo luồng

Hướng dẫn này là Phần 3 của một loạt bài gồm ba phần "Học PHP" dạy bạn cách sử dụng PHP thông qua việc xây dựng một ứng dụng tiến trình công việc đơn giản. Trong hướng dẫn này, bạn sẽ học về sử dụng xác thực HTTP, tạo luồng các tập tin và cách tạo các đối tượng và các ngoại lệ.

Tyler Anderson, Kỹ sư, Backstop Media

Tyler's photoTyler Anderson đã tốt nghiệp chuyên ngành Khoa học Máy tính tại trường Đại học Brigham Young năm 2004 và hiện đang tham gia học kỳ cuối chương trình đào tạo Thạc sỹ khoa học chuyên ngành Công nghệ Máy tính. Trước đó, anh đã làm việc như là một lập trình viên cơ sở dữ liệu cho DPMG.COM, và hiện tại anh đang là kỹ sư cho tập đoàn Stexar, dựa trên nền tảng Beaverton, Oregon



Nicholas Chase, Tác giả tự do, Site Dynamics Interactive Communications

Nicholas Chase đã phát triển trang web cho các công ty lớn như Lucent Technologies, Sun Microsystems, Oracle, và Tampa Bay Buccaneers. Nick đã từng là một giáo viên vật lý ở trường phổ thông, một nhà quản lý thiết bị phóng xạ mức thấp, một nhà biên tập tạp chí khoa học viễn tưởng trực tuyến, một kỹ sư đa phương tiện, một hướng dẫn của Oracle, và một trưởng phòng công nghệ của một công ty tương tác truyền thông. Nick là tác giả của một số sách



30 09 2010

Trước khi bạn bắt đầu

Về hướng dẫn này

Hướng dẫn này kết thúc ứng dụng tiến trình công việc đơn giản mà bạn đã bắt đầu trong phần đầu tiên của loạt bài này về học PHP. Bạn sẽ thêm xác thực HTTP, khả năng tạo luồng tài liệu từ một vị trí truy cập không là trang Web và xử lý ngoại lệ. Bạn cũng sẽ thiết lập một số các ứng dụng trong các đối tượng.

Nói chung, bạn sẽ thêm khả năng cho một người quản trị để phê duyệt một tập tin, làm cho nó sẵn sàng cho những người dùng. Các chủ đề sau sẽ được thảo luận:

  • Việc cho phép và sử dụng xác thực HTTP dựa trên trình duyệt.
  • Tạo luồng dữ liệu từ một tập tin.
  • Tạo các lớp và các đối tượng.
  • Sử dụng các phương thức và các đặc tính của đối tượng.
  • Tạo và xử lý các ngoại lệ.
  • Sử dụng các thuộc tính định danh của XML
  • Xác nhận tính hợp lệ một tài liệu XML bằng cách sử dụng DTD (Định nghĩa kiểu tài liệu - Document Type Definition).
  • Kiểm soát truy cập dữ liệu dựa trên các trang yêu cầu.

Ai cần sử dụng hướng dẫn này?

Hướng dẫn này là Phần 3 của loạt bài ba phần được thiết kế để dạy cho bạn những điều cơ bản về lập trình trong PHP trong khi xây dựng một ứng dụng tiến trình công việc đơn giản. Hướng dẫn này dành cho các nhà phát triển, những người muốn tìm hiểu thêm về các chủ đề nâng cao, chẳng hạn như sử dụng PHP để lập trình hướng đối tượng. Hướng dẫn này cũng đề cập đến việc xác thực HTTP, tạo luồng, các lớp và các đối tượng và xử lý ngoại lệ, cũng như cung cấp cái nhìn khác về thao tác XML.

Hướng dẫn này giả định bạn đã có một sự hiểu biết với các khái niệm cơ bản về PHP, chẳng hạn như cú pháp, xử lý biểu mẫu và truy cập một cơ sở dữ liệu. Bạn có thể nhận được các thông tin mà bạn cần nhờ hướng dẫn "Học PHP, Phần 1" và "Học PHP, Phần 2," xem phần Tài nguyên.

Các điều kiện cần có trước

Để làm theo mã ví dụ, bạn cần bảo đảm các công cụ sau đây được cài đặt và chạy thử:

Máy chủ HTTP -- Bạn có thể cài đặt PHP trên nhiều máy chủ HTTP như Apache và Microsoft® IIS, và trên Windows®, Linux®, UNIX®, Mac OS X, và các nền tảng khác. Nói chung, sự lựa chọn của bạn về máy chủ không quan trọng, nhưng hướng dẫn này sẽ trình bày một số vấn đề cấu hình liên quan đến việc xác thực HTTP bằng cách sử dụng Apache 2.x như là một ví dụ. Bạn có thể tải về máy chủ Apache HTTP từ Apache.

PHP -- Tất nhiên, bạn sẽ cần một bản phân phối của PHP. Cả hai phiên bản V4 và V5 của PHP đang sử dụng tại thời điểm viết bài này, nhưng hướng dẫn này tập trung vào phiên bản V5 do những cải tiến nâng cao của nó. Hãy tải PHP.

Cơ sở dữ liệu -- Một phần của dự án này liên quan đến việc lưu dữ liệu vào một cơ sở dữ liệu, do đó tất nhiên bạn cũng sẽ cần một cơ sở dữ liệu. Hướng dẫn này trình bày cơ sở dữ liệu MySQL vì nó thường được dùng với PHP. Bạn có thể tải MySQL từ http://dev.mysql.com/downloads/index.html.


Câu chuyện cho đến nay

Bây giờ mọi thứ đã được đặt đúng chỗ

Bạn đã xây dựng một ứng dụng tiến trình công việc đơn giản thông qua diễn biến của các hướng dẫn này. Ứng dụng này cho phép người dùng tải các tập tin lên hệ thống và xem các tập tin đó, cũng như các tập tin đã được một quản trị viên hệ thống phê duyệt. Cho đến nay, bạn đã xây dựng:

  • Một trang đăng ký cho phép người dùng sử dụng một biểu mẫu HTML để đăng ký tài khoản bằng cách nhập vào một tên người dùng duy nhất, địa chỉ e-mail và mật khẩu. Bạn đã xây dựng trang PHP để phân tích các dữ liệu được đệ trình, kiểm tra cơ sở dữ liệu để đảm bảo rằng tên người dùng là duy nhất và lưu đăng ký trong cơ sở dữ liệu.
  • Một trang đăng nhập nhận tên và mật khẩu người dùng, kiểm tra chúng so với cơ sở dữ liệu và, nếu chúng được xác nhận hợp lệ, tạo một phiên làm việc trên máy chủ để máy chủ biết tập tin nào cần hiển thị.
  • Các thành phần giao diện đơn giản cho phép phát hiện người dùng được đăng nhập chưa để hiển thị các lựa chọn thích hợp.
  • Một trang tải lên cho phép người dùng gửi một tập tin đến máy chủ thông qua trình duyệt. Bạn cũng xây dựng trang để lấy tập tin tải lên và lưu nó vào máy chủ, sau đó thêm thông tin về nó vào một tập tin XML để phục hồi sau này, bằng cách sử dụng DOM (Mô hình đối tượng tài liệu).
  • Một hàm hiển thị cho phép đọc tập tin XML và hiển thị thông tin nhờ API đơn giản với XML (SAX).

Bạn có thể tải các tập tin biểu thị nơi ứng dụng ngừng lại trong "Học PHP, Phần 2."

Bạn sẽ làm gì

Trước khi bạn đi qua hướng dẫn này, bạn sẽ có -- tất nhiên, dù rất đơn giản -- một ứng dụng tiến trình công việc đầy đủ. Trong hướng dẫn này, bạn sẽ:

  • Thêm xác thực HTTP, được máy chủ Web kiểm soát. Bạn cũng sẽ tích hợp quá trình đăng ký của bạn để nó thêm người dùng mới vào máy chủ Web.
  • Thêm các liên kết đến hàm hiển thị các tập tin có sẵn để những người dùng có thể tải chúng. Bạn sẽ tạo một hàm cho phép tạo luồng cho các tập tin này tới trình duyệt từ vị trí truy cập không là trang Web.
  • Xác nhận rằng những người sử dụng tải các tập tin từ trang thích hợp. Bạn sẽ sử dụng thực tế là các tập tin phải được ứng dụng tạo luồng, thay vì chỉ cần máy chủ HTTP phục vụ, cho phép kiểm soát các trường hợp trong đó những người sử dụng tải các tập tin
  • Tạo một lớp thể hiện một tài liệu và sử dụng các phương thức hướng đối tượng để truy cập và tải nó.
  • Tạo và sử dụng các ngoại lệ tùy chỉnh để trợ giúp các vấn đề cần chính xác.
  • Thêm thông tin vào tập tin XML sẽ cho phép bạn là người duy nhất nhận biết mỗi tập tin. Bước này sẽ cho phép bạn thêm các hộp kiểm tra vào biểu mẫu hiển thị cho phép những người quản trị hệ thống có thể sử dụng để xác định duyệt các tập tin nào.
  • Quản lý tiến trình phê duyệt, điều chỉnh tập tin XML của bạn để bạn có thể yêu cầu trực tiếp các phần tử fileInfo cụ thể.

Hãy bắt đầu bằng cách thay đổi dáng vẻ chung về những gì bạn đã có.

Trang chào mừng

Đến nay, bạn đã tập trung xây dựng các mẩu ứng dụng riêng của bạn. Bây giờ là lúc để bắt đầu kết hợp chúng lại với nhau, do đó hãy bắt đầu với trang chào mừng đơn giản mà bạn có thể sử dụng như một "dải hạ cánh" cho những người truy cập. Tạo một tập tin mới gọi là index.php và thêm vào:

<?php 
  include ("top.txt"); 
  include ("scripts.txt");

  display_files(); 
  
  include ("bottom.txt"); 
?>

Hàm include() đầu tiên nạp các thành phần giao diện trên cùng cho trang đó và thiết lập một phiên làm việc, nếu thích hợp. Hàm include () thứ hai nạp tất cả các kịch bản lệnh mà bạn đã tạo cho đến nay, bao gồm hàm display_files() mà bạn đã tạo trong "Học PHP, Phần 2," đã liệt kê tất cả các tập tin được người dùng hiện tại tải lên hoặc đã được người quản trị phê duyệt. Hàm include cuối cùng chỉ là phần dưới cùng của trang HTML.

Lưu tập tin này trong cùng thư mục với các tập tin khác bạn đã tạo ra. Ví dụ, bạn có thể đặt tập tin đó trong thư mục gốc tài liệu của máy chủ của bạn, do đó một khi bạn đã khởi động máy chủ HTTP, bạn có thể xem trang đó bằng cách trỏ trình duyệt của bạn đến http://localhost/index.php.

Như bạn thấy trong Hình 1, trang này khá đơn giản và, trên thực tế nếu bạn đã tiếp tục đi, bạn không nên xem bất kỳ các tập tin nào, trừ khi bạn đã đăng nhập vì chưa gì được phê duyệt cả. Các tập tin ở đây đã được phê duyệt cho mục đích trình diễn.

Hình 1. Trang liệt kê cơ bản
Trang liệt kê cơ bản

Nếu bạn chỉ mới khởi động trình duyệt của bạn, bạn sẽ thấy các liên kết RegisterLogin vì bạn chưa đăng nhập. Trong phần tiếp theo, bạn sẽ xem một cách khác để xử lý quá trình đó.


Sử dụng xác thực HTTP

Xác thực HTTP

Cho đến nay, bạn đã sử dụng hệ thống đăng nhập trong đó người dùng nhập tên và mật khẩu vào một biểu mẫu và khi họ đệ trình biểu mẫu, thông tin đó được kiểm tra so với cơ sở dữ liệu MySQL. Nếu nó phù hợp, ứng dụng này tạo một phiên làm việc trong PHP và gán một tên người dùng cho mảng $_SESSION để sử dụng sau này.

Trong khi tiến trình này làm việc tốt, bạn gặp một vấn đề khi bạn tích hợp với các hệ thống khác. Ví dụ, nếu ứng dụng tiến trình công việc của bạn đã là một phần của một mạng nội bộ trong đó những người dùng có thể đăng nhập với các tên người dùng từ các hệ thống khác, bạn có thể không muốn yêu cầu họ đăng nhập lại. Thay vào đó, bạn muốn họ đã được đăng nhập rồi khi đã ở trong mạng, nếu họ đã đăng nhập ở nơi khác. Điều này được gọi là hệ thống đăng nhập một lần.

Để thực hiện điều đó ở đây, bạn sẽ chuyển sang một hệ thống có máy chủ Web thực sự kiểm soát quá trình đăng nhập. Thay vì chỉ đơn giản dùng trang này, máy chủ kiểm tra tên người dùng và mật khẩu trong lúc có yêu cầu từ trình duyệt và nếu máy chủ không thấy chúng, máy chủ lệnh cho trình duyệt bật lên một hộp thoại tên người dùng và mật khẩu để bạn có thể nhập thông tin. Một khi nhập thông tin, bạn sẽ không phải thực hiện nó lại vì trình duyệt sẽ gửi nó cùng với các yêu cầu tiếp theo.

Hãy bắt đầu bằng cách thiết lập máy chủ.

Kích hoạt xác thực HTTP

Trước khi bắt đầu, bạn hãy lưu ý nếu sử dụng máy chủ khác Apache 2.x, bạn nên kiểm tra tài liệu về xác thực HTTP để xem bạn cần làm gì để thiết lập nó. (Hoặc là bạn có thể đơn giản bỏ qua phần này. Bạn sẽ có sẵn các bước thích hợp để cho ứng dụng làm việc với bất kì kiểu xác thực nào).

Nhưng việc xác thực HTTP thực sự làm việc ra sao? Trước hết, máy chủ biết nó cần loại bảo mật nào để cung cấp cho mỗi thư mục. Một cách thay đổi điều đó với một thư mục cụ thể là thiết lập các thứ trong cấu hình chính cho máy chủ. Một cách khác là sử dụng một tập tin .htaccess, có chứa các hướng dẫn với thư mục có tập tin đó.

Ví dụ, bạn muốn máy chủ đảm bảo rằng tất cả những người dùng truy cập các tập tin cho riêng người dùng của bạn có các tên người dùng và các mật khẩu hợp lệ, do đó trước tiên tạo một thư mục gọi là loggedin bên trong thư mục có chứa tập tin hiện tại của bạn. Ví dụ, nếu tập tin của bạn nằm trong /usr/local/apache2/htdocs, bạn sẽ tạo thư mục /usr/local/apache2/htdocs/loggedin.

Bây giờ bạn cần báo cho máy chủ biết rằng bạn sắp phủ lên an ninh tổng thể với thư mục đó, do đó hãy mở tập tin:

<Directory /usr/local/apache2/htdocs/loggedin> 
    AllowOverride AuthConfig 
</Directory>

(Tất nhiên, bạn nên sử dụng thư mục đúng cho thiết lập riêng của mình).

Bây giờ là lúc chuẩn bị thư mục thực sự.

Thiết lập việc xác thực

Tiếp theo, tạo một tập tin văn bản mới và lưu tập tin đó trong thư mục loggedin với tên là .htaccess. Thêm vào đó:

AuthName "Registered Users Only" 
AuthType Basic 
AuthUserFile /usr/local/apache2/password/.htpasswd 
Require valid-user

Chúng ta hãy thực hiện điều này từ đầu. AuthName là văn bản xuất hiện ở đầu của hộp thoại tên người dùng và mật khẩu AuthType xác định bạn đang sử dụng xác thực kiểu Basic, có nghĩa bạn sẽ gửi tên người dùng và mật khẩu theo dạng văn bản rõ. AuthUserFile là tập tin chứa các tên đăng nhập và các mật khẩu cho phép. (Bạn sẽ tạo tập tin đó ngay). Cuối cùng, chỉ dẫn Require để cho bạn xác định người thực sự xem nội dung này. Ở đây, bạn sẽ nói rằng bạn sẽ hiển thị nó cho bất kỳ người dùng hợp lệ nào, nhưng bạn cũng có tùy chọn để quy định những người dùng hoặc nhóm người dùng cụ thể.

Khởi động lại máy chủ HTTP để những thay đổi này có hiệu lực.

(Đối với Apache V2.0, gọi <APACHE_HOME>/bin/apachectl stop, tiếp theo là <APACHE_HOME>/bin/apachectl start.)

Tiếp theo, bạn sẽ tạo tập tin mật khẩu.

Tạo tập tin mật khẩu

Để tất cả điều này hoạt động, bạn cần có tập tin mật khẩu để máy chủ có thể kiểm tra nó. Trong phần Thêm những người dùng mới vào, vào tập tin mật khẩu, bạn sẽ xem xét thao tác tập tin này từ bên trong PHP, nhưng bây giờ, nếu bạn có quyền truy cập vào dòng lệnh, bạn có thể trực tiếp tạo tập tin đó.

Trước tiên, chọn một vị trí cho tập tin .htpasswd của bạn. Tập tin đó không có trong thư mục có thể truy cập trang Web. Tập tin đó cũng không thật an toàn khi có ai đó chỉ cần tải về và phân tích nó. Tập tin đó cũng phải ở tại nơi mà PHP có thể viết vào nó. Ví dụ, bạn có thể tạo một thư mục mật khẩu trong thư mục apache2 của bạn. Bất kể bạn chọn vị trí nào, hãy đảm bảo bạn có thông tin chính xác trong tập tin .htaccess của bạn.

Để tạo tập tin mật khẩu, hãy thực hiện lệnh sau, thay thế thư mục và tên người dùng riêng của bạn:

htpasswd -c /usr/local/apache2/password/.htpasswd roadnick

Sau đó bạn được nhắc gõ vào, rồi lặp lại mật khẩu, như sau:

htpasswd -c /usr/local/apache2/password/.htpasswd roadnick 
New password: 
Re-type new password: 
Adding password for user roadnick

Khóa chuyển đổi -c lệnh cho máy chủ biết để tạo một tập tin mới, vì thế sau khi bạn đã thêm người dùng mới, tập tin đó sẽ trông giống như sau:

roadnick:IpoRzCGnsQv.Y

Lưu ý rằng phiên bản này của mật khẩu được mã hóa và bạn phải nhớ nó khi thêm các mật khẩu từ ứng dụng của bạn.

Bây giờ hãy xem phiên bản này hoạt động.

Đăng nhập

Để xem phiên bản này hoạt động, bạn cần truy cập một tập tin trong thư mục được bảo vệ. Chuyển các tập tin uploadfile.php và uploadfile_action.php vào thư mục loggedin và sao chép index.php vào thư mục loggedin như là display_files.php.

Trong mỗi một trong ba tập tin trên, hãy thay đổi các câu lệnh include() thành tài khoản cho vị trí mới, như sau:

<?php 
      include ("../top.txt"); 
      include ("../scripts.txt");  
      
      echo "Logged in user is ".$_SERVER['PHP_AUTH_USER']; 
      
      display_files(); 
      include ("../bottom.txt"); 
?>

Trong trường hợp này, bạn sửa chữa các tham chiếu tới các tập tin kèm theo, nhưng bạn cũng tham chiếu một biến cần được thiết lập khi trình duyệt gửi tên người dùng và mật khẩu. Trỏ trình duyệt của bạn tới http://localhost/loggedin/display_files.php để xem việc này hoạt động. Như bạn thấy trong Hình 2, bạn sẽ nhận được một hộp thoại tên người dùng và mật khẩu.

Hình 2. Hộp thoại tên đăng nhập và mật khẩu
Hộp thoại tên đăng nhập và mật khẩu

Nhập tên người dùng và mật khẩu mà bạn thường dùng trong phần Tạo tập tin mật khẩu để xem trang thực tế.

Sử dụng thông tin đăng nhập

Tại thời điểm này, bạn đã nhập tên người dùng và đăng nhập, vì thế bạn có thể xem trang đó. Tuy nhiên, như bạn có thể thấy trong Hình 3, mặc dù thông báo cho biết người sử dụng đã đăng nhập, nhưng nội dung thực sự có vẻ không đúng. Bạn vẫn thấy các liên kết Register (Đăng ký) và Login (Đăng nhập), và danh sách các tập tin vẫn còn hiển thị chỉ những tập tin mà người quản trị hệ thống đã phê duyệt -- chứ không phải những tập tin mà người dùng hiện tại đã tải lên và vẫn đang chờ giải quyết.

Hình 3. Loại Logged in ...
Loại Logged in ...

Để giải quyết những vấn đề này, bạn có hai lựa chọn. Thứ nhất là quay trở lại và mã hóa lại mọi trường hợp trong đó ứng dụng tham chiếu tên người dùng để tìm $_SERVER['PHP_AUTH_USER'], thay cho $_SESSION["username"]. Tuy nhiên, những lập trình viên giỏi vốn lười biếng, vì thế đây không là tùy chọn đặc biệt hấp dẫn.

Sự lựa chọn thứ hai là chỉ cần thiết lập $_SESSION["username"] dựa vào $_SERVER['PHP_AUTH_USER'] để mọi thứ sẽ tiếp tục làm việc như đã làm trước đây. Bạn có thể làm điều này trong tập tin top.txt, ngay sau khi bạn bắt đầu một phiên làm việc mới hoặc kết nối vào một phiên làm việc hiện có:

<? 
  session_start(); 
  if (isset($_SESSION["username"])){ 
      //Do nothing 
  } elseif (isset($_SERVER['PHP_AUTH_USER'])) { 
          $_SESSION["username"] = $_SERVER['PHP_AUTH_USER']; 
  }  
?> 
<html> 
  <head>
      <title>Workflow System</title> 
  </head> 
<body>

Trước hết, cách duy nhất để làm cho trình duyệt "quên" tên người dùng và mật khẩu bạn đã nhập là đóng trình duyệt đó lại để bạn đưa ra quyền ưu tiên của biến $_SESSION["username"]. Bằng cách đó, bạn có tùy chọn để cho phép những người dùng đăng nhập như là người khác. (Bạn sẽ không làm điều đó ở đây, nhưng bạn có tùy chọn đó).

Tiếp theo, nếu chưa thiết lập cả biến $_SESSION lẫn biến $_SERVER, thì không có điều gì xảy ra và trang đó vẫn tiếp tục như thể người dùng không đăng nhập -- điều xảy ra là một trường hợp. Việc làm cho trường hợp này thành một thay đổi đơn giản sẽ sửa chữa vấn đề đăng nhập của bạn, như bạn có thể thấy trong Hình 4.

Hình 4. Trang đã sửa chữa
Trang đã sửa chữa

Thay đổi giao diện

Trước khi bạn tiếp tục thêm một người dùng mới, bạn cần thực hiện một số thay đổi nhanh chóng cho tập tin top.txt để điều chỉnh cấu trúc mới. Trước hết, bạn cần thay đổi liên kết Login để thay vì trỏ vào trang login.php cũ của bạn, nó trỏ đến tập tin display_files.php vừa mới được bảo mật. Khi người dùng cố gắng truy cập tập tin đó, trình duyệt sẽ cung cấp một cách để đăng nhập:

... 
<tr> 
    <td width="30%" valign="top">
        <h3>Navigation</h3> 

<?php if (isset($_SESSION["username"]) || isset($username)){ 
?>
   <p>You are logged in as <?=$_SESSION["username"].$username?>. 
<!-- You can <a href="/logout.php">logout</a> to login as a different user.--></p>
               
   <p><a href="/loggedin/uploadfile.php">Upload a file</a></p>
   <p><a href="/loggedin/display_files.php">Display files</a></p> 

<?php 
  } else { 
?> 
   <p><a href="/registration.php">Register</a></p> 
   <p><a href="/loggedin/display_files.php">Login</a></p>

<?php 
  } 
?>
     </td>

Lưu ý rằng ngoài việc thay đổi tham chiếu đăng nhập và thêm một tùy chọn mới để hiển thị danh sách các tập tin, chúng tôi đã giải thích qua thông báo về đăng xuất, do chủ đề đó vượt quá phạm vi của hướng dẫn này.

Bây giờ bạn chỉ cần tích hợp quá trình đăng ký với tập tin mật khẩu.

Thêm những người dùng mới vào tập tin mật khẩu

Bước cuối cùng trong quá trình này là tích hợp đăng ký của bạn với tập tin .htpasswd. Để làm điều đó, bạn chỉ cần thêm một mục mới ngay khi bạn đã lưu người sử dụng đó vào cơ sở dữ liệu. Mở tập tin registration_action.php và thêm vào như sau:

... 
$passwords = $_POST["pword"]; 
$sql = "insert into users (username, email, password) values 
        ('" .$_POST["name"]."', '".$_POST["email"] ."', '".$passwords[0]."')"; 
$result = mysql_query($sql); 

if ($result){ 
   echo "It's entered!"; 

   $pwdfile = '/usr/local/apache2/password/.htpasswd'; 
       if (is_file($pwdfile)){ 
          $opencode = "a"; 
       } else { $opencode = "w"; 
       } 
       $fp = fopen($pwdfile, $opencode); 
       $pword_crypt = crypt($passwords[0]); 
       fwrite($fp, $_POST['name'].":".$pword_crypt."\n"); 
       fclose($fp); 
    } else { 
         echo "There's been a problem: ".mysql_error(); 
    } 
  } else { 
       echo "There is already a user with that name. Please try again. <br />"; 
...

Trước khi bạn bắt đầu, nếu bạn đã có một tập tin .htpasswd rồi, hãy đảm bảo rằng người dùng có thể viết vào tập tin đó trên máy chủ Web của bạn. Nếu không, hãy chắc chắn rằng người dùng có thể viết vào thư mục thích hợp.

Trước hết, hãy kiểm tra xem tập tin đó có tồn tại không và sử dụng thông tin đó để xác định xem bạn sẽ viết một tập tin mới hoặc nối thêm thông tin vào một tập tin hiện có không. Một khi bạn biết rồi, hãy tiếp tục và mở tập tin đó.

Như bạn đã thấy trong phần Tạo tập tin mật khẩu, mật khẩu này được lưu giữ dưới dạng đã mã hóa, vì vậy bạn có thể sử dụng hàm crypt() để nhận được chuỗi đó. Cuối cùng, viết tên người dùng và mật khẩu vào tập tin đó và đóng tập tin lại.

Để kiểm tra điều này, hãy thoát khỏi trình duyệt để xóa bất kỳ mật khẩu được nhớ nhanh nào, rồi mở http://localhost/index.php.

Nhấn vào Register và tạo một tài khoản mới. Khi bạn đã hoàn thành việc tạo tài khoản, thoát khỏi trình duyệt một lần nữa và thử truy cập vào một trang được bảo vệ. Tên đăng nhập và mật khẩu mới sẽ làm việc.


Sử dụng các luồng

Các luồng là gì?

Bây giờ bạn đã có hệ thống được thiết lập, bạn đã sẵn sàng để cho phép người dùng thực sự tải về các tập tin có sẵn. Tuy nhiên, ngay từ đầu, những tập tin này đã được lưu trữ trong một thư mục có thể truy cập được không phải trang Web, vì vậy một liên kết đơn giản tới các tập tin đó vượt ra ngoài điều đang bàn đến.

Thay vào đó, trong phần này, bạn sẽ tạo ra một hàm để tạo luồng cho tập tin từ vị trí hiện tại của nó tới trình duyệt.

Bây giờ, cách bạn thực sự truy cập nguồn tài nguyên, chẳng hạn như một tập tin, tùy thuộc vào nơi và cách thức lưu tập tin. Việc truy cập vào một tập tin vị trí là rất khác với việc truy cập vào tập tin trên một máy chủ ở xa thông qua HTTP hoặc FTP.

Tuy nhiên, may mắn thay, PHP cung cấp các trình bao bọc luồng (stream wrappers). Nói cách khác, bạn thực hiện một cuộc gọi đến một tài nguyên, bất cứ nơi nào nó có và nếu PHP có một trình bao bọc (wrapper) có hiệu lực, PHP sẽ tìm ra cách để thực hiện cuộc gọi đó.

Bạn có thể tìm các trình bao bọc có sẵn bằng cách in các nội dung của mảng được hàm stream_get_wrappers() trả về, giống như sau:

<?php 
print_r(stream_get_wrappers()); 
?>

Hàm print_r() rất dễ sử dụng để xem các nội dung của một mảng. Ví dụ, hệ thống của bạn có thể cung cấp cho bạn:

 Array ( 
   [0] => php 
   [1] => file 
   [2] => http 
   [3] => ftp 
)

Điều này sẽ cho phép bạn dễ dàng lưu trữ các tập tin của bạn trên một máy chủ Web hoặc máy chủ FTP ở xa như là một sự thay thế cho việc lưu trữ chúng như là các tập tin trên máy chủ nội bộ và mã được bạn sử dụng trong phần này sẽ vẫn làm việc.

Chúng ta hãy xem xét.

Tải tập tin

Đối với người dùng để có thể xem một tập tin, trình duyệt phải thu nhận nó. Trình duyệt cũng phải biết tập tin nào để hiển thị nó đúng. Bạn có thể quan tâm đến cả hai vấn đề này. Hãy tạo một tập tin mới gọi là download_file.php và lưu nó trong thư mục loggedin. Thêm vào như sau:

<?php 
  include ("../scripts.txt"); 

  $filetype = $_GET['filetype']; 
  $filename = $_GET['file']; 
  $filepath = UPLOADEDFILES.$filename;

  if($stream = fopen($filepath, "rb")){ 
    $file_contents = stream_get_contents($stream);
    header("Content-type: ".$filetype); 
    print($file_contents); 
  } 
?>

Không kể sức mạnh của nó, ở đây quá trình này trong thực tế là khá đơn giản. Trước tiên, bạn mở tập tin để đọc và để đưa vào bộ đệm. Điều bạn đang thực sự làm với hàm fopen() sẽ tạo ra một nguồn tài nguyên biểu thị tập tin đó. Sau đó bạn có thể chuyển tài nguyên đó tới hàm stream_get_contents(), để đọc toàn bộ tập tin ra thành một chuỗi đơn.

Bây giờ bạn có nội dung, bạn có thể gửi nó vào trình duyệt, nhưng trình duyệt sẽ không biết phải làm gì với nội dung đó và có khả năng sẽ hiển thị nó dưới dạng văn bản. Điều đó là tốt cho một tập tin văn bản, nhưng không tốt cho một tập tin ảnh hoặc thậm chí một tập tin HTML. Vì vậy, thay vì chỉ gửi đi nội dung thô, trước tiên bạn gửi một header (tiêu đề) cho trình duyệt với thông tin về Content-type (kiểu-nội dung) của tập tin, chẳng hạn như image/jpeg.

Cuối cùng, bạn chỉ đơn giản xuất ra các nội dung của tập tin cho trình duyệt. Khi đã nhận phần tiêu đề Content-type, trình duyệt sẽ biết để không chỉ xử lý nó như là một trang Web (tất nhiên, trừ khi nó một trang Web).

Cho đến lúc quyết định thực sự sử dụng tập tin nào và kiểu nào, bạn sẽ đọc chúng từ mảng $_GET, vì vậy bạn có thể thêm chúng ngay vào URL, như sau:

http://localhost/loggedin/download_file.php?file=timeone.jpg&filetype=image/jpeg

Nhập URL này (tất nhiên với một tên tập tin và kiểu thích hợp) vào trình duyệt của bạn để xem kết quả được hiển thị trong Hình 5.

Hình 5. Tải một tập tin
Tải một tập tin

Thêm một liên kết đến tập tin

Bởi vì tất cả các thông tin mà trang tải xuống cần có thể được thêm vào URL, thật đơn giản để thêm một liên kết cho phép người dùng tải về một tập tin. Bạn tạo sự hiển thị các tập tin có sẵn bằng cách sử dụng một luồng SAX, có nghĩa là kết quả đầu ra thực tế được kiểm soát bởi một lớp của trình xử lý nội dung được gọi, trong trường hợp này, là Content_Handler. Mở tập tin scripts.txt và thêm mã sau vào lớp Content_Handler:

class Content_Handler{ 
  private $available = false; 
  private $submittedBy = ""; 
  private $status = ""; 

  private $currentElement = ""; 

  private $fileName = ""; 
  private $fileSize = ""; 
  private $fileType = "";
 
  function start_element($parser, $name, $attrs){
   ... 
  }
 
  function end_element($parser, $name){ 
     if ($name == "workflow"){ 
        echo "</table>"; 
     } 
     if ($name == "fileInfo"){ 
       echo "<tr><td><a href='download_file.php?file=".$this->fileName."
             &filetype=".$this->fileType."'>" 
                     .$this->fileName."</a> </td>"."
                  <td>".$this->submittedBy."</td>"."
                  <td>".$this->fileSize."</td>"."
                  <td>".$this->status."</td>
                 </tr>"; 
           $this->fileName = "";
           $this->submittedBy = ""; 
           $this->fileSize = ""; 
           $this->status = "";
           $this->fileType = ""; 

           $this->available = false; 
        }
     $this->currentElement = ""; 
  } 
  
  function chars($parser, $chars){ 

    if ($this->available){ 
       if ($this->currentElement == "fileName"){
               $this->fileName = $this->fileName . $chars; 
       } if ($this->currentElement == "fileType"){ 
             $this->fileType = $this->fileType . $chars; 
          } 
          if ($this->currentElement == "size"){ 
                $this->fileSize = $this->fileSize .$chars; 
          } 
       } 
    } 
  }

Ngoài những thông tin mà bạn đã theo dõi với mỗi phần tử fileInfo, bây giờ bạn cần phải theo dõi phần tử fileType, vì vậy bạn cần phải thêm một đặc tính cho phần tử đó.

Với hàm chars(), bạn lưu giá trị khi bạn bắt đầu nó. Khi bạn đi tới cuối phần tử fileInfo và đó là lúc để hiển thị các thông tin, bạn sử dụng hàm đó, cùng với fileName (tên tập tin), để tạo ra một liên kết trỏ đến trang tải xuống. Bạn có thể xem kết quả trong Hình 6.

Hình 6. Liên kết đến tập tin
Liên kết đến tập tin

Nhấn vào một liên kết để kiểm tra tập tin.

Tiếp theo, bạn sẽ xem xét việc đóng gói quá trình này thành một đối tượng.


Sử dụng các đối tượng

Vậy các đối tượng là gì?

Trong phần này, bạn sẽ xem xét việc sử dụng các đối tượng. Cho đến nay, hầu như tất cả mọi thứ mà bạn đã làm đều theo thủ tục, có nghĩa là bạn có một kịch bản lệnh hầu như chạy từ đầu đến cuối. Bây giờ bạn sẽ di chuyển ra khỏi đó.

Khái niệm trung tâm của lập trình hướng đối tượng là ý tưởng mà bạn có thể biểu thị "những thứ" như một gói độc lập. Ví dụ, một ấm điện có các đặc tính, chẳng hạn như màu sắc và nhiệt độ tối đa của nó và các khả năng, chẳng hạn như làm nóng nước và tự tắt.

Nếu bạn đã biểu thị rằng ấm đun nước như là một đối tượng, nó cũng sẽ có các đặc tính như color (màu sắc) và maximumTemperature (nhiệt độ tối đa) và các khả năng -- hoặc các phương thức -- như heatWater() (làm nóng nước) và turnOff() (tự tắt). Nếu bạn đang viết một chương trình cho phép giao tiếp với ấm đun nước, bạn chỉ cần gọi phương thức heatWater() của đối tượng ấm đun nước, hơn là lo lắng về cách nó thực hiện trong thực tế.

Để làm cho những điều này rõ hơn một chút, bạn sẽ tạo ra một đối tượng biểu thị một tập tin được tải xuống. Nó sẽ có các đặc tính, chẳng hạn như tên gọi và kiểu tập tin và các phương thức, chẳng hạn như download().

Tuy nhiên, khi đã nói tất cả điều đó, chúng ta cần chỉ ra rằng bạn chưa thực sự định nghĩa một đối tượng. Thay vào đó, bạn định nghĩa một lớp của các đối tượng. Một lớp hoạt động như một loại "khuôn mẫu" cho các đối tượng của kiểu đó. Sau đó bạn tạo ra một thể hiện của lớp đó và biểu hiện đó là đối tượng.

Hãy bắt đầu bằng cách tạo ra lớp thực tế.

Tạo lớp WFDocument

Bước đầu tiên trong việc xử lý các đối tượng là tạo lớp mà trên đó các đối tượng dựa vào. Bạn có thể thêm định nghĩa này vào tập tin scripts.txt, nhưng bạn đang cố gắng để tạo mã có khả năng duy trì được nhiều hơn, chứ không ít hơn. Vì vậy, tạo một tập tin riêng biệt, WFDocument.php và lưu nó trong thư mục chính. Thêm vào như sau:

<?php 
include_once("scripts.txt"); 

class WFDocument { 
   function download($filename, $filetype) { 
      $filepath = UPLOADEDFILES.$filename; 
      if($stream = fopen($filepath, "rb")){ 
            $file_contents = stream_get_contents($stream);
            header("Content-type: ".$filetype); 
            print($file_contents); 
      } 
   } 
} 
?>

Trước tiên, bạn cần hằng số UPLOADEDFILES, vì vậy bạn bao gồm tập tin scripts.txt. Tiếp theo, bạn tạo ra lớp thực tế. Lớp WFDocument chỉ có một phương thức duy nhất là, download(), giống như mã trong tập tin download_file.php, ngoại trừ việc nhận tên và kiểu tập tin làm các đầu vào cho hàm đó hơn là trực tiếp trích xuất chúng khỏi mảng $_GET array.

Bây giờ hãy xem xét việc tạo lớp này

Gọi đối tượng WFDocument-type

Bạn đã thực sự đã tạo một số đối tượng khi bạn đang làm việc với DOM trong Phần 2 của loạt bài này, nhưng chúng tôi đã không nói nhiều về lý do tại sao hoặc như thế nào. Chúng tôi sẽ khắc phục điều đó bây giờ.

Mở trang download_file.php và thay đổi mã để nó đọc như sau:

<?php
  include ("../WFDocument.php"); 

  $filetype = $_GET['filetype']; 
  $filename = $_GET['file']; 

  $wfdocument = new WFDocument();
  $wfdocument->download($filename, $filetype); 

?>

Trước hết, thay vì bao gồm tập tin scripts.txt, bạn sẽ bao gồm định nghĩa của lớp WFDocument mà bạn đưa vào tập tin WFDocument.php. (Một số các nhà phát triển tìm thấy nó có ích để chỉ tạo một trang bao gồm tất cả các lớp của họ, sau đó bao gồm trang đó chứ không phải bao gồm tất cả các lớp riêng biệt ở khắp nơi).

Bây giờ bạn đã sẵn sàng để tạo ra một đối tượng mới, mà bạn làm bằng cách sử dụng từ khoá new (mới). Dòng này tạo một đối tượng mới theo kiểu WFDocument và gán nó vào biến $wfdocument.

Một khi bạn có một tham chiếu đến đối tượng đó, bạn có thể gọi bất kỳ phương thức chung nào của nó. Trong trường hợp này, chỉ có một phương thức, download(), và bạn gọi nó bằng cách sử dụng toán tử ->. Về cơ bản, biểu tượng này có nghĩa, "Sử dụng phương thức (hoặc đặc tính) thuộc về đối tượng này".

Lưu tập tin và kiểm tra nó bằng cách nhấn vào một trong các liên kết trên trang của bạn. Mã giống hệt như nó đã có trước đó. Sự khác biệt duy nhất là cách bạn đang gọi nó.

Tạo các đặc tính

Tất nhiên, các phương thức chỉ là một phần của câu chuyện. Phần bao trùm của một đối tượng là nó được đóng gói. Nói cách khác, nó phải chứa tất cả thông tin riêng của mình, vì thế thay vì cung cấp tên và loại tập tin cho phương thức download(), bạn có thể đặt chúng như là các đặc tính về đối tượng. Nhưng trước tiên bạn cần tạo chúng trong lớp đó:

<?php 

include_once("../scripts.txt"); 

class WFDocument { 
   public $filename; 
   public $filetype; 
   function download() { 
      $filepath = UPLOADEDFILES.$this->filename; 
      if($stream = fopen($filepath, "rb")){
          $file_contents = stream_get_contents($stream); 
          header("Content-type: ".$this->filetype); 
          print($file_contents); 
      } 
   } 
} 
?>

Lưu ý rằng bạn khai báo các biến bên ngoài hàm; chúng là một phần của lớp và không phải là hàm. Bạn cũng khai báo chúng là public (chung), có nghĩa là bạn có thể truy cập chúng từ chính bên ngoài lớp đó. Bạn cũng có thể thiết lập một đặc tính là private (riêng), có nghĩa là bạn có thể sử dụng nó chỉ trong chính lớp đó hoặc protected (được bảo vệ), có nghĩa là bạn có thể sử dụng nó chỉ trong lớp đó hoặc bất kỳ các lớp nào dựa vào đặc tính này. (Nếu bạn không quen với ý tưởng này, hãy kiên nhẫn chờ đợi một lát. Chúng tôi sẽ nói thêm về khái niệm này, quyền thừa kế (inheritance) , trong phần Tạo một ngoại lệ tùy chỉnh.)

Cuối cùng, để tham khảo một đặc tính đối tượng, bạn phải biết đặc tính đó thuộc về đối tượng nào. Trong bản thân đối tượng, bạn có thể chỉ cần sử dụng từ khoá $this, trong đó đề cập đến chính đối tượng đó. Bằng cách này, bạn có thể sử dụng $this->filename để tham chiếu đặc tính filename của đối tượng thực thi đoạn mã này.

Bây giờ hãy xem xét việc thiết lập các giá trị cho các đặc tính này.

Thiết lập các đặc tính

Thay vì chuyển thông tin tới một đối tượng, bạn muốn thực sự thiết lập các đặc tính của đối tượng đó:

<?php 
  include ("../WFDocument.php"); 

  $filetype =  $_GET['filetype']; 
  $filename = $_GET['file']; 

  $wfdocument = new WFDocument(); 
  $wfdocument->filename = $filename; 
  $wfdocument->filetype = $filetype;
  $wfdocument->download();
 
?>

Lưu ý ghi chú ở đây. Bạn đang sử dụng tên đối tượng là $wfdocument, toán tử -> và tên của đặc tính. Một khi các đặc tính này đã được thiết lập, chúng có sẵn từ bên trong đối tượng, do đó bạn không phải chuyển chúng đến phương thức download().

Bây giờ, khi đã thực hiện tất cả điều đó, trong thực tế có một cách tốt hơn để xử lý loại điều này, vì vậy hãy xem xét một cách khác.

Ẩn các đặc tính

Mặc dù dĩ nhiên có thể trực tiếp thiết lập giá trị của một đặc tính, như bạn đã làm trong phần trước, không có cách tốt nhất để xử lý những thứ này. Thay vào đó, thói quen chung là ẩn giấu các đặc tính thực tế khỏi dùng chung và sử dụng getterssetters để nhận và thiết lập các giá trị của chúng, giống như sau:

<?php 
   include_once("../scripts.txt"); 

   class WFDocument {

         private $filename; 
         private $filetype; 
 
         function  setFilename($newFilename){ 
               $this->filename = $newFilename; 
         } 
         function   getFilename(){ 
               return $this->filename; 
         } 
         function setFiletype($newFiletype){
               $this->filetype = $newFiletype; 
         } 
         function getFiletype(){ 
              return $this->filetype; 
         } 

         function download() { 
             $filepath = UPLOADEDFILES.$this-> getFilename() 
             if($stream = fopen($filepath, "rb")){
                $file_contents = stream_get_contents($stream); 
                header("Content-type: ".$this->getFiletype()) 
                print($file_contents); 
             } 
         } 
   } 
?>

Trước tiên, bạn định nghĩa các đặc tính là private. Điều đó có nghĩa là nếu bạn cố gắng thiết lập chúng trực tiếp, như bạn đã đang làm, bạn sẽ nhận được một lỗi. Nhưng bạn vẫn phải thiết lập các giá trị này, nên thay vì bạn sử dụng các phương thức getFilename(), setFilename(), getFiletype()setFiletype(). Lưu ý rằng bạn sử dụng chúng ở đây theo phương thức download(), cũng giống như bạn đã sử dụng đặc tính ban đầu.

Việc sử dụng getters và setters là tiện dụng vì nó cho phép bạn kiểm soát nhiều hơn qua những gì đang xảy ra với dữ liệu của bạn. Ví dụ, bạn có thể muốn thực hiện các việc kiểm tra xác nhận hợp lệ nào đó trước khi bạn cho phép thiết lập một giá trị cụ thể cho một đặc tính.

Gọi các đặc tính ẩn

Bây giờ bạn đã ẩn các đặc tính, bạn cần phải quay lại và chỉnh sửa trang download_file.php để bạn không nhận được một lỗi:

<?php 
      include ("../WFDocument.php"); 

      $filetype = $_GET['filetype']; 
      $filename = $_GET['file']; 

      $wfdocument = new WFDocument();
      $wfdocument->setFilename($filename);
      $wfdocument->setFiletype($filetype); 
      $wfdocument->download();

?>

Cách tiếp cận này là có ích, những có những cách dễ dàng hơn để thiết lập các đặc tính cho một đối tượng..

Tạo một hàm tạo

Nếu một đối tượng có một hàm tạo (constructor), thì nó sẽ được gọi mỗi khi bạn tạo ra một biểu hiện mới của lớp cụ thể đó. Ví dụ, bạn có thể tạo một hàm tạo đơn giản:

... 
  function getFiletype(){ 
     return $this->filetype; 
  } 

  function __construct(){ 
     echo "Creating new WFDocument"; 
  } 

  function download() { 
     $filepath = UPLOADEDFILES.$this->filename; 
...

Nếu bạn cố gắng chạy như là kịch bản lệnh này, bạn sẽ thấy một lỗi vì đối tượng đó xuất văn bản (Creating new WFDocument) (Tạo WFDocument mới) trước khi xuất các tiêu đề, như bạn thấy trong Hình 7.

Hình 7. Lỗi sau khi chạy kịch bản lệnh
Lỗi sau khi chạy kịch bản lệnh

Vì vậy, ngay cả khi bạn chưa bao giờ gọi công khai phương thức __construct(), ứng dụng này đã gọi nó ngay khi đối tượng được tạo. Bạn có thể sử dụng điều đó cho lợi thế của bạn bằng cách thêm thông tin vào hàm tạo.

Tạo một đối tượng có thông tin

Một trong những cách sử dụng phổ biến nhất với một hàm tạo là cung cấp một cách để khởi tạo các giá trị khác nhau khi bạn tạo đối tượng. Ví dụ, bạn có thể thiết lập lớp WFDocument để bạn thiết lập các đặc tính filenamefiletype khi bạn tạo đối tượng:

... 
function getFiletype(){ 
   return $this->filetype; 
} 
function __construct($filename = "", $filetype = ""){
   $this->setFilename($filename); 
   $this->setFiletype($filetype); 
}
function download() { 
   $filepath = UPLOADEDFILES.$this->filename; 
...

Khi bạn tạo đối tượng, PHP thực hiện bất kỳ hướng dẫn nào trong hàm tạo trước khi tiếp tục. Trong trường hợp này, hàm tạo đó đang tìm kiếm filenamefiletype. Nếu bạn không cung cấp chúng, bạn vẫn sẽ không phải nhận một lỗi, bởi vì bạn đã xác định các giá trị mặc định để sử dụng nếu không có giá trị nào được đưa ra khi hàm được gọi.

Nhưng bạn công khai gọi hàm __construct()?

Tạo đối tượng: Gọi các hàm tạo

Trên thực sự bạn không công khai gọi phương thức hàm tạo. Thay vào đó, bạn ngầm gọi nó mỗi khi bạn tạo một đối tượng. Điều đó có nghĩa là bạn sử dụng thời điểm cụ thể đó để chuyển thông tin cho các hàm tạo:

<?php 
      include ("../WFDocument.php"); 

      $filetype = $_GET['filetype']; 
      $filename = $_GET['file']; 

      $wfdocument = new WFDocument($filename, $filetype); 
      $wfdocument->download(); 

?>

Bất kỳ thông tin được chuyển cho lớp đó khi bạn tạo đối tượng mới được chuyển tới hàm tạo. Theo cách này, bạn có thể chỉ cần tạo đối tượng và sử dụng nó để tải về tập tin.


Xử lý các trường hợp ngoại lệ

Một ngoại lệ chung

Vì các ngoại lệ bắt đầu hoạt động khi một cái gì đó không hoàn toàn đúng với một ứng dụng, chúng thường bị nhầm lẫn với các lỗi. Tuy nhiên, các ngoại lệ linh hoạt hơn nhiều. Trong phần này, bạn sẽ thấy cách xác định các loại ngoại lệ khác nhau và sử dụng chúng để xác định điều sắp xảy ra với ứng dụng.

Hãy bắt đầu với một ngoại lệ chung đơn giản trong định nghĩa của lớp WFDocument:

<?php 

include_once("../scripts.txt"); 
  
class WFDocument { 
  ...
  function download() { 
    $filepath = UPLOADEDFILES.$this->filename; 
      try {
         if(file_exists($filepath)){ 
            if ($stream = fopen($filepath, "rb")){
               $file_contents = stream_get_contents($stream); 
               header("Content-type: ".$this->filetype); 
               print($file_contents); 
            }  
         } else { throw new Exception ("File '".$filepath."' does not exist."); 
         } 
       } catch (Exception $e) { 
           echo "<p style='color: red'>".$e->getMessage()."</p>"; 
       } 
    } 
  } 
?>

Trước hết, các ngoại lệ không chỉ xảy ra, chúng được ném ra. Và, tất nhiên, nếu bạn ném một cái gì đó, bạn cần phải bắt giữ nó, vì vậy bạn tạo ra một câu lệnh try-catch (thử-bắt giữ). Trong phần try (thử), bạn đưa ra mã của mình. Nếu một cái gì đó không thích hợp xảy ra, chẳng hạn như, trong trường hợp này, một tập tin không tồn tại và bạn ném ra một ngoại lệ, PHP di chuyển ngay tới khối catch để bắt giữ ngoại lệ đó.

Một ngoại lệ có nhiều đặc tính, chẳng hạn như dòng và tập tin mà từ đó các ngoại lệ được ném ra và một thông báo. Thông thường, ứng dụng thiết lập thông báo khi nó ném ra ngoại lệ, như bạn thấy ở đây. Bản thân ngoại lệ, $e, sau đó cung cấp văn bản đó bằng cách sử dụng phương thức getMessage(). Ví dụ, nếu bạn cố gắng để tải về một tập tin không tồn tại, bạn sẽ thấy thông báo màu đỏ được hiển thị, như trong Hình 8.

Hình 8. Ngoại lệ cơ bản
Ngoại lệ cơ bản

Tuy nhiên, sức mạnh thực sự của các ngoại lệ xuất phát từ việc tạo riêng của bạn.

Tạo một ngoại lệ tùy chỉnh

Trong phần cuối, bạn kiểm tra các đối tượng, nhưng chúng tôi đã bỏ sót một khía cạnh rất quan trọng của chúng: đó là quyền thừa kế. Hãy xem xét điều đó bây giờ.

Một lợi thế để sử dụng các lớp là khả năng sử dụng một lớp làm cơ sở cho lớp khác. Ví dụ, bạn có thể tạo một loại ngoại lệ mới, NoFileExistsException, mở rộng lớp Exception (ngoại lệ) ban đầu:

class NoFileExistsException extends Exception { 

  public function informativeMessage(){ 
     $message = "The file, '".$this->getMessage()."', called on line ". 
     $this->getLine()." of ".$this->getFile().", does not exist."; 
     return $message; 
  } 
}

(Để đơn giản, chúng tôi đã thêm mã này vào tập tin WFDocument.php, nhưng bạn có thể thêm nó vào bất cứ nơi nào nó có thể truy cập được khi bạn cần nó).

Ở đây, bạn đã tạo ra một lớp mới là, NoFileExistsException, với một phương thức duy nhất: informativeMessage(). Trong thực tế, lớp này cũng là một Exception, vì vậy tất cả những phương thức và đặc tính chung nào cho một đối tượng Exception cũng có sẵn.

Ví dụ, lưu ý rằng trong hàm informativeMessage(), bạn gọi phương thức getLine()getFile(), mặc dù chúng không được định nghĩa ở đây. Chúng được định nghĩa trong lớp cơ sở, Exception, vì vậy bạn có thể sử dụng chúng.

Bây giờ hãy xem ngoại lệ đang hoạt động.

Bắt giữ một ngoại lệ tùy chỉnh

Cách dễ nhất để sử dụng loại ngoại lệ mới là đơn giản ném nó ra đúng như bạn muốn ném một Exception chung:

function download() { 
  $filepath = UPLOADEDFILES.$this->filename;
  try { 
     if(file_exists($filepath)){ 
        if ($stream = fopen($filepath, "rb")){
            $file_contents = stream_get_contents($stream); 
            header("Content-type: ".$this->filetype); 
            print($file_contents); 
        } 
      } else { 
          throw new NoFileExistsException ($filepath); 
      } 
  } catch (NoFileExistsException $e) { 
      echo "<p style='color: red'>".$e->informativeMessage()."</p>"; 
  } 
}

Lưu ý rằng ngay cả khi bạn chỉ chuyển $filepath khi bạn tạo ngoại lệ đó, bạn được thông báo đầy đủ trở lại, như trong Hình 9.

Hình 9. Sử dụng một ngoại lệ tùy chỉnh
Sử dụng một ngoại lệ tùy chỉnh

Làm việc với nhiều ngoại lệ

Một lý do để tạo các lớp của ngoại lệ tùy chỉnh là bạn có thể sử dụng khả năng của PHP để phân biệt giữa chúng. Ví dụ, bạn có thể tạo nhiều khối catch cho một try duy nhất:

 ... 
function download() { 
   $filepath = UPLOADEDFILES.$this->filename; 
   try { 
     if(file_exists($filepath)){ 
       if ($stream = fopen($filepath, "rb")){ 
           $file_contents = stream_get_contents($stream);
           header("Content-type: ".$this->filetype); 
           print($file_contents); 
       } else { 
             throw new Exception ("Cannot open file ".$filepath); 
            } 
      } else { 
            throw new NoFileExistsException ($filepath); 
      } 
   } catch (NoFileExistsException $e) { 
       echo "<p style='color: red'>".$e->informativeMessage()."</p>"; 
   } catch (Exception $e){ 
          echo "<p style='color: red'>".$e->getMessage()."</p>"; 
      } 
   } 
}

Trong trường hợp này, bạn cố gắng bắt giữ các vấn đề trước khi chúng xảy ra bằng cách kiểm tra sự tồn tại của tập tin và ném ra một NoFileExistsException. (ngoại lệ tập tin không tồn tại). Nếu bạn biết trước rằng rào cản hay cái gì khác ngăn bạn mở tập tin đó, bạn ném ra một ngoại lệ chung. PHP phát hiện các loại ngoại lệ mà bạn ném ra và thực thi các khối catch thích hợp.

Tất cả điều này có vẻ hơi quá một chút để chỉ xuất ra các thông báo, nhưng đó không phải tất cả những thứ mà bạn có thể làm. Bạn có thể tạo các phương thức tùy chỉnh cho ngoại lệ của bạn, ví dụ, gửi các thông báo cho các sự kiện cụ thể. Bạn cũng có thể tạo các khối catch tùy chỉnh để thực hiện các hành động khác nhau tùy theo tình hình.

Tất nhiên, chỉ vì bạn đã định nghĩa tất cả các ngoại lệ khác nhau không có nghĩa là bạn phải bắt giữ từng ngoại lệ riêng biệt, như bạn sẽ thấy tiếp theo.

Lan truyền các ngoại lệ

Một tính năng tiện dụng của quyền thừa kế là khả năng xử lý một đối tượng như thể nó đã là một thành viên của lớp cơ sở của nó. Ví dụ, bạn có thể ném ra một NoFileExistsException và bắt giữ nó như là một Exception chung:

 ... 
function download() { 
  $filepath = UPLOADEDFILES.$this->filename; 

  try { 
    if(file_exists($filepath)){ 
      if ($stream = fopen($filepath, "rb")){ 
         $file_contents = stream_get_contents($stream);
         header("Content-type: ".$this->filetype); 
         print($file_contents); 
      } else { 
          throw new Exception ("Cannot open file ".$filepath); 
      } 
     } else { throw new NoFileExistsException ($filepath); 
     } 
   } catch (Exception $e){ 
          echo "<p style='color: red'>".$e->getMessage()."</p>"; 
   } 
  } 
}

Trong trường hợp này, khi bạn ném ra ngoại lệ đó, PHP hoạt động theo cách của nó đi xuôi xuống danh sách các khối catch, tìm kiếm một khối catch đầu tiên để áp dụng. Ở đây bạn chỉ có một khối, nhưng khối đó sẽ bắt giữ bất kỳException nào, như trong Hình 10.

Hình 10. Lan truyền các ngoại lệ
Lan truyền các ngoại lệ

Đặt mọi thứ lại với nhau

Bạn cần làm gì

Bây giờ bạn đã có quá trình tải tập tin tại chỗ, đó là lúc để đặt tất cả mọi thứ lại với nhau và hoàn thành ứng dụng. Trong phần này, bạn sẽ quan tâm đến một số nhiệm vụ linh tinh vẫn còn cần thực hiện:

  • Tạo các trình định danh tập tin riêng.
  • Phát hiện những người quản trị hệ thống.
  • Tạo biểu mẫu để cho phép người quản trị hệ thống phê duyệt các tập tin.
  • Kiểm tra phần tải xuống để đảm bảo chúng vẫn chưa được gọi từ máy chủ khác.

Bắt đầu bằng cách tạo ra một lớp Counter (bộ đếm).

Xác định các tài liệu cá nhân

Cho đến nay, bạn đã không lo lắng về việc xác định các tập tin cụ thể, trừ khi bạn đang tải chúng, nhưng bây giờ bạn cần phải chú ý nhiều hơn một chút nữa. Cuối cùng, bạn sẽ xử lý một biểu mẫu để cho phép một người quản trị phê duyệt các tập tin cụ thể, do đó sẽ thật có ích để có một cách dễ dàng để tham khảo chúng.

Điều bạn sắp làm ở đây là tạo một lớp Counter để cho phép bạn tạo một khóa duy nhất cho mỗi tập tin. Sau đó bạn thêm khóa đó vào tập tin XML, cho phép bạn trực tiếp yêu cầu phần tử fileInfo thích hợp. Bạn bắt đầu bằng cách tạo ra định nghĩa lớp Counter. Ví dụ, bạn có thể đặt nó trong tập tin scripts.txt:

class Counter{ 

  function getNextId(){ 
     $filename = "/usr/local/apache2/htdocs/counter.txt"; 
     $handle = fopen($filename, "r+"); 
     $contents = fread($handle, filesize($filename)); 

     $nextid = $contents + 1; 
     echo $nextid;
     rewind($handle); 
     fwrite($handle, $nextid); 
     fclose($handle); 

     return $nextid; 
  }
}

Ở đây bạn có một hàm đơn, getNextId(), để đọc một tập tin hiện có, counter.txt và tăng các nội dung lên 1. (Vì vậy, trước khi bạn bắt đầu, hãy tạo tập tin với mục nhập đơn là 0.) Sau đó hàm này quay ngược lên đầu của tập tin và viết giá trị mới để giá trị đó sẽ có mặt khi bạn gọi hàm lần tới.

Bạn sử dụng lớp này khi bạn thêm thông tin tập tin vào tập tin XML.

Thêm trình định danh vào tập tin XML

Cuối cùng, bạn muốn có thể lấy ra một phần tử fileInfo đơn theo thuộc tính ID của nó, vì thế hãy tiếp tục và thêm thông tin này vào tập tin docinfo.xml được tạo ra trong "Học PHP, Phần 2."

function save_document_info($fileInfo){ 
   $xmlfile = UPLOADEDFILES."docinfo.xml"; 
   ... 
   $filename = $fileInfo['name']; 
   $filetype = $fileInfo['type']; 
   $filesize = $fileInfo['size']; 

   $fileInfo = $doc->createElement("fileInfo"); 
 
   $counter = new Counter();
   $fileInfo->setAttribute("id", "_".$counter->getNextId());

   $fileInfo->setAttribute("status", "pending");
   $fileInfo->setAttribute("submittedBy", getUsername()); 
   ...
   $doc->save($xmlfile); 
}

Mỗi khi thông tin của một tài liệu mới được lưu, bạn tạo một đối tượng Counter và sử dụng phương thức getNextId() của nó để cung cấp một giá trị duy nhất cho thuộc tính id. Vì sau đó bạn sẽ xác định thuộc tính này theo kiểu ID, bạn sẽ định trước giá trị với một dấu gạch dưới (_) vì các giá trị này không thể bắt đầu bằng một số.

Các kết quả trông giống như sau (chúng tôi thêm vào khoảng trống để làm cho nó dễ đọc hơn một chút):

<fileInfo id="_13" status="pending" submittedBy="roadnick"> 
  <approvedBy/>
  <fileName>timeone.jpg</fileName>
  <location>/var/www/hidden/</location>
  <fileType>image/jpeg</fileType> 
  <size>2020</size>
</fileInfo>

Lưu ý rằng quá trình này không ảnh hưởng đến bất kỳ dữ liệu hiện có nào của bạn, vì vậy bằng cách thủ công bạn cần thêm vào các thuộc tính id cho tất cả các phần tử fileInfo của bạn hoặc xóa tập tin docinfo.xml và bắt đầu lại, tải lên các tập tin để làm việc với chúng.

Bây giờ bạn đã sẵn sàng phê duyệt các tập tin, nhưng trước tiên bạn cần phải thiết lập những người quản trị hệ thống để thực hiện việc đó.

Phát hiện những người quản trị hệ thống

Khi lúc đầu bạn đã tạo bảng người sử dụng trong cơ sở dữ liệu, bạn đã không tính đến thực tế là bạn đã cần phân biệt giữa những người sử dụng thường xuyên với những người quản trị hệ thống, vì vậy bạn phải quan tâm đến điều đó bây giờ. Đăng nhập vào MySQL và thực hiện các lệnh sau:

alter table users add status varchar(10) default 'USER'; 
update users set status = 'USER'; 
update users set status = 'ADMIN' where id=3;

Lệnh đầu tiên thêm cột mới, status (trạng thái), vào bảng users (những người dùng). Bạn không xác định loại người dùng trên trang đăng ký, vì vậy bạn chỉ cần xác định một giá trị mặc định của USER cho bất kỳ người dùng mới nào được bổ sung vào hệ thống. Lệnh thứ hai thiết lập trạng thái này cho những người sử dụng hiện có. Cuối cùng, bạn chọn một người sử dụng làm người quản trị. (Hãy chắc chắn sử dụng giá trị id thích hợp cho dữ liệu của bạn).

Bây giờ bạn có dữ liệu, bạn có thể tạo một hàm để trả về trạng thái của người sử dụng hiện tại:

function getUserStatus(){ 
   $username = $_SESSION["username"];
   db_connect(); 
   $sql = "select * from users where username='".$username."'"; 

   $result = mysql_query($sql); 
   $row = mysql_fetch_array($result); 

   $status = ""; 

   if ($row) {
      $status = $row["status"]; 
   } else { 
      $status = "NONE"; 
   } 
   mysql_close(); 
   return $status;
}

Chúng ta hãy xem lại cách quá trình này hoạt động như thế nào. Trước tiên, bạn tạo một kết nối đến cơ sở dữ liệu phù hợp bằng cách sử dụng kịch bản lệnh mà bạn đã tạo: db_connect(). Sau đó bạn có thể tạo ra một câu lệnh SQL bằng cách sử dụng tên người dùng, được lưu trữ trong biến $_SESSION. Tiếp theo, bạn thực hiện câu lệnh đó và cố gắng để có được dòng dữ liệu (và có lẽ chỉ một dòng) đầu tiên.

Nếu có một hàng tồn tại, bạn thiết lập trạng thái bằng giá trị của cột status. Nếu không, bạn thiết lập trạng thái bằng NONE (không). Cuối cùng, đóng kết nối và trả về giá trị.

Đặt hàm này trong tập tin scripts.txt để bạn có thể truy cập nó khi bạn hiển thị danh sách tập tin.

Phê duyệt tập tin đó: Biểu mẫu

Bây giờ bạn đã sẵn sàng để thêm các khả năng phê duyệt cho biểu mẫu đó. Điều bạn muốn là hiển thị một hộp kiểm tra cho các tập tin đang chờ giải quyết nếu người dùng xem danh sách các tập tin là người quản trị hệ thống. Lớp Content_Handler xử lý hiển thị này:

class Content_Handler{ 
  private $available = false; 
  private $submittedBy = ""; 
  private $status = ""; 

  private $currentElement = ""; 

  private $fileId = ""; 
  private $fileName = ""; 
  private $fileSize = ""; 
  private $fileType = ""; 
  private $userStatus = "";
 
  function start_element($parser, $name, $attrs){ 
    if ($name == "workflow"){ 
      $this->userStatus = getUserStatus($_SESSION["username"]); 
      if ($this->userStatus == "ADMIN"){ 
        echo "<form action='approve_action.php' method='POST'>"; 
      } 
      echo "<h3>Available files</h3>"; 
      echo "<table width='100%' border='0'><tr>". "<th>File Name</th><th>Submitted
               By</th>". "<th>Size</th><th>Status</th>"; 
      if ($this->userStatus == "ADMIN"){ 
          echo "<th>Approve</th>"; 
      } 
      echo "</tr>"; 
    } 
    if ($name == "fileInfo"){ 
      if ($attrs['status'] == "approved" || $attrs['submittedBy'] == $this->username){ 
        $this->available = true; 
      } 
      if ($this->available){ 
        $this->submittedBy = $attrs['submittedBy'];
        $this->status = $attrs['status']; 
        $this->fileId = $attrs['id']; 
      } 
    }
    $this->currentElement = $name; 
  } 

  function end_element($parser, $name){ 
    if ($name == "workflow"){ 
      echo "</table>"; 
      if ($this->userStatus == "ADMIN"){ 
        echo "<input type='submit' value='Approve Checked Files' />"; 
        echo "</form>"; 
      } 
    } 
    if ($name == "fileInfo"){ 
      echo "<tr>"; 
      echo "<td><a href='download_file.php?file=". $this->fileName."&filetype=".
               $this->fileType."'>". $this->fileName."</a></td>".
               "<td>".$this->submittedBy."</td>".
               "<td>".$this->fileSize."</td>".
                  "<td>".$this->status."</td><td>"; 
      if ($this->userStatus == "ADMIN"){ 
        if ($this->status == "pending") { 
          echo "<input type='checkbox' name='toapprove[]' value='". $this->fileId."
                 ' checked='checked' />"; 
        } 
      }
      echo "</td></tr>"; 
      $this->fileId = "";
      $this->fileName = ""; 
      $this->submittedBy = ""; 
      $this->fileSize = "";
      $this->status = ""; 
      $this->fileType = ""; 
      $this->available = false; 
    }
    $this->currentElement = ""; 
  } 

  function chars($parser, $chars){ 
    ... 
  } 
}

Bắt đầu từ trên cùng, bạn có hai đặc tính mới để định nghĩa: $fileId$userStatus. Đặc tính sau, bạn thiết lập chỉ một lần, khi bạn xử lý sự khởi đầu của phần tử workflow và, vì thế, là sự khởi đầu của tài liệu. Tại thời điểm này, nếu người dùng một người quản trị, bạn muốn thêm vào một phần tử form biểu mẫu vào trang đó và một cột Approve (phê duyệt) vào bảng đó.

Bạn đóng biểu mẫu ở cuối của tài liệu, khi trình xử lý nội dung nhận được thông báo về sự kết thúc của phần tử workflow.

Đối với các hộp kiểm tra thực tế, bạn xuất các hộp này ra khi bạn hiển thị hàng thông tin ở cuối của mỗi phần tử fileInfo. Vì bạn có tiềm năng cho nhiều mục nhập, bạn đặt tên trường là toapprove[].

Kết quả là một biểu mẫu với các hộp thích hợp, như bạn có thể thấy trong Hình 11.

Hình 11. Biểu mẫu phê duyệt
Biểu mẫu phê duyệt

Thiết lập các ID

Bây giờ bạn đã có biểu mẫu, nhưng để truy cập vào các phần tử fileInfo bằng các thuộc tính id của chúng, bạn cần phải thêm nhiều hơn một bước. Không giống như trong một tài liệu HTML, chỉ cần đặt tên một thuộc tính "id" là chưa đủ để làm cho nó hành động giống như một trình định danh. Trong một tập tin XML, bạn phải cung cấp một số loại lược đồ (chú ý chữ "s" nhỏ) để định nghĩa thuộc tính. Trong trường hợp này, bạn sẽ thêm một DTD (Định nghĩa kiểu tài liệu). Trước tiên, bạn thêm một tham chiếu đến nó trong tài liệu thực tế:

function save_document_info($fileInfo){
 
  $xmlfile = UPLOADEDFILES."docinfo.xml";
 
  if(is_file($xmlfile)){ 
    $doc = DOMDocument::load($xmlfile); 
    $workflowElements = $doc->getElementsByTagName("workflow"); 
    $root = $workflowElements->item(0);

    $statistics = $root->getElementsByTagName("statistics")->item(0); 
    $total = $statistics->getAttribute("total"); 
    $statistics->setAttribute("total", $total + 1); 
  } else{  
      $domImp = new DOMImplementation; 
      $dtd = $domImp->createDocumentType('workflow', '', 'workflow.dtd'); 
      $doc = $domImp->createDocument("", "", $dtd); 
      $root = $doc->createElement('workflow'); 
      $doc->appendChild($root); 
      $statistics = $doc->createElement("statistics"); 
      $statistics->setAttribute("total", "1");
      $statistics->setAttribute("approved", "0"); 
      $root->appendChild($statistics); 
   }
  ... 
}

Thay vì tạo tài liệu trực tiếp bằng cách tạo lớp DOMDocument, bạn tạo một DOMImplementation, từ đó bạn tạo một đối tượng DTD. Sau đó bạn gán DTD đó cho tài liệu mới mà bạn đang tạo.

Nếu bạn gỡ bỏ tập tin docinfo.xml và tải lên một tài liệu mới, bạn sẽ thấy thông tin mới:

<?xml version="1.0"?> 
<!DOCTYPE workflow SYSTEM "workflow.dtd"> 
<workflow> <statistics total="3" approved="0"/>
 ...

Bây giờ bạn cần phải tạo tập tin workflow.dtd.

DTD

Việc giải thích tất cả các sắc thái của việc xác nhận hợp lệ XML là vượt quá xa phạm vi của hướng dẫn này, nhưng bạn cần phải có một DTD để mô tả cấu trúc của tập tin docinfo.xml. Để làm điều đó, hãy tạo một tập tin và lưu nó như là tập tin workflow.dtd trong cùng một thư mục với tập tin docinfo.xml. Thêm vào như sau:

<!ELEMENT workflow (statistics, fileInfo*) > 
<!ELEMENT statistics EMPTY> 
<!ATTLIST statistics total CDATA #IMPLIED 
                          approved CDATA #IMPLIED > 
<!ELEMENT fileInfo (approvedBy, fileName, location, fileType, size)> 
<!ATTLIST fileInfo id ID #IMPLIED> 
<!ATTLIST fileInfo status CDATA #IMPLIED> 
<!ATTLIST fileInfo submittedBy CDATA #IMPLIED>
<!ELEMENT approvedBy (#PCDATA)> 
<!ELEMENT fileName (#PCDATA)>
<!ELEMENT location (#PCDATA)> 
<!ELEMENT fileType (#PCDATA)> 
<!ELEMENT size (#PCDATA)>

Để đơn giản, bạn định nghĩa mỗi phần tử và mô hình "nội dung"của nó. Ví dụ, phần tử workflow phải có một phần tử con statistics và không có hoặc có nhiều phần tử con fileInfo hơn nữa.

Bạn cũng định nghĩa các thuộc tính và các kiểu của chúng. Ví dụ, phần tử statistics có hai thuộc tính tùy chọn, total (tổng số) và approved (được phê duyệt) và chúng đều là dữ liệu ký tự.

Ở đây khóa là định nghĩa của giá trị id của phần tử fileInfo, mà bạn đã định nghĩa theo loại ID.

Bây giờ bạn có thể sử dụng thông tin này.

Phê duyệt tập tin: Cập nhật XML

Trang biểu mẫu thực tế chấp nhận các hộp kiểm tra phê duyệt, approve_action.php, rất đơn giản:

<?php 
  include "../scripts.txt"; 

  $allApprovals = $_POST["toapprove"]; 
  foreach ($allApprovals as $thisFileId) {
    approveFile($thisFileId); 
  } 
  echo "Files approved."; 

?>

Đối với mỗi hộp kiểm tra toapprove, bạn chỉ cần gọi hàm approveFile(), trong scripts.txt:

function approveFile($fileId){ 

  $xmlfile = UPLOADEDFILES."docinfo.xml"; 

  $doc = new DOMDocument(); 
  $doc->validateOnParse = true; 
  $doc->load($xmlfile); 

  $statisticsElements = $doc->getElementsByTagName("statistics"); 
  $statistics = $statisticsElements->item(0);
 
  $approved = $statistics->getAttribute("approved"); 
  $statistics->setAttribute("approved", $approved+1); 

  $thisFile = $doc->getElementById($fileId);
  $thisFile->setAttribute("status", "approved"); 

  $approvedByElements = $thisFile->getElementsByTagName("approvedBy"); 
  $approvedByElement = $approvedByElements->item(0);
  $approvedByElement->appendChild($doc->createTextNode($_SESSION["username"]));
  $doc->save($xmlfile); 
}

Trước khi bạn còn tải các tài liệu, bạn xác định rằng bạn muốn trình phân tích cú pháp xác nhận hợp lệ nó hoặc kiểm tra nó so với DTD. Điều này thiết lập tính chất của thuộc tính id. Một khi bạn đã tải tập tin, bạn sẽ có được một tham chiếu đến phần tử statistics để bạn có thể tăng số lượng các tập tin được phê duyệt.

Bây giờ bạn đã sẵn sàng để thực sự phê duyệt tập tin. Vì bạn thiết lập thuộc tính id như là một giá trị kiểu - ID, nên bạn có thể sử dụng getElementById() để yêu cầu phần tử fileInfo thích hợp. Khi bạn có phần tử đó, bạn có thể thiết lập status (trạng thái) của nó tới approved.

Bạn cũng cần có được một tham chiếu tới phần tử con approvedBy của phần tử này. Khi bạn đã có tham chiếu đó, bạn có thể thêm một phần tử con của nút Text mới với tên người dùng của người quản trị.

Cuối cùng, lưu tập tin.

Lưu ý rằng trong khi bạn đã thực hiện tập tin theo cách này để đơn giản, thì trong một ứng dụng sản xuất, hiệu quả hơn là mở và tải tập tin chỉ một lần, tạo ra tất cả các thay đổi, sau đó lưu tập tin.

Kiểm tra an ninh lúc tải về

Là bước cuối cùng, bạn thêm một việc kiểm tra an ninh cho quá trình tải về. Vì bạn kiểm soát toàn bộ quá trình này thông qua ứng dụng, nên bạn có thể sử dụng bất cứ việc kiểm tra nào bạn muốn. Ví dụ này, bạn sẽ kiểm tra để đảm bảo rằng người dùng đã nhấn vào liên kết với một tập tin trên một trang ở trên máy chủ nội bộ của bạn, ngăn người khác khỏi liên kết đến nó từ một trang web bên ngoài hoặc thậm chí ngăn nó khỏi đánh dấu liên kết hoặc gửi cho một ai đó một liên kết thô.

Bạn bắt đầu bằng cách tạo một ngoại lệ mới, đúng lúc này, trong tập tin WFDocument.php:

<?php 
  include_once("../scripts.txt"); 

class NoFileExistsException extends Exception { 
    public function informativeMessage(){
      $message = "The file, '".$this->getMessage()."', called on line ".
               $this->getLine()." of ".$this->getFile().", does not exist."; 
      return $message;
    } 
} 

class ImproperRequestException extends Exception {
   public function logDownloadAttempt(){ 
      //Additional code here 
     echo "Notifying administrator ..."; 
   }
} 

class WFDocument { 
  private $filename; 
  private $filetype; 

  function setFilename($newFilename){ 
    $this->filename = $newFilename; 
  } 
  function getFilename(){ 
    return $this->filename; 
  } 
  function setFiletype($newFiletype){
    $this->filetype = $newFiletype; 
  } 
  function getFiletype(){ 
    return $this->filetype; 
  } 
  function __construct($filename = "", $filetype = ""){
    $this->setFilename($filename); 
    $this->setFiletype($filetype); 
  } 

  function download() { 
    $filepath = UPLOADEDFILES.$this->filename; 
    try {  
        $referer = $_SERVER['HTTP_REFERER']; 
        $noprotocol = substr($referer, 7, strlen($referer));
        $host = substr($noprotocol, 0, strpos($noprotocol, "/")); 
        if ( $host != 'boxersrevenge' && $host != 'localhost'){ 
          throw new ImproperRequestException("Remote access not allowed. 
                   Files must be accessed from the intranet."); 
        } 
        if(file_exists($filepath)){ 
          if ($stream = fopen($filepath, "rb")){ 
            $file_contents = stream_get_contents($stream); 
            header("Content-type: ".$this->filetype); print($file_contents); 
          } else { 
              throw new Exception ("Cannot open file ".$filepath);  
          } 
        } else { 
            throw new NoFileExistsException ($filepath); 
        }
     } catch (ImproperRequestException $e){ 
         echo "<p style='color: red'>".$e->getMessage()."</p>"; 
         $e->logDownloadAttempt(); 
       }  catch (Exception $e){ 
            echo "<p style='color: red'>".$e->getMessage()."</p>"; 
       } 
   } 
} 
?>

Trước hết, trong ImproperRequestException, bạn tạo một phương thức mới là, logDownloadAttempt(), để có thể gửi một e-mail hoặc thực hiện một số hành động khác. Bạn sử dụng phương thức đó trong khối catch của kiểu ngoại lệ này.

Trong hàm download() thực tế, điều đầu tiên bạn làm là nhận HTTP_REFERER. Phần tiêu đề của tùy chọn này được gửi với một yêu cầu web xác định trang để thực hiện yêu cầu từ đó. Ví dụ, nếu bạn liên kết với developerWorks từ blog của bạn và bạn nhấn vào liên kết đó, các bản ghi của IBM sẽ hiển thị các URL của blog của bạn như là HTTP_REFERER cho truy cập đó.

Trong trường hợp của bạn, bạn muốn chắc chắn yêu cầu sẽ đến từ ứng dụng của bạn, vì vậy đầu tiên bạn gỡ bỏ chuỗi "http://" ở đầu, sau đó lưu tất cả văn bản cho đến dấu gạch chéo ngược đầu tiên (/). Đây là tên máy chủ trong yêu cầu.

Đối với một yêu cầu bên ngoài, tên máy chủ này có thể là một cái gì đó dọc theo các dòng boxersrevenge.nicholaschase.com, nhưng bạn đang tìm kiếm chỉ các yêu cầu nội bộ, vì vậy bạn chấp nhận boxersrevenge hay localhost. Nếu yêu cầu đến từ bất cứ nơi nào khác, bạn ném ra ImproperRequestException, để khối thích hợp bắt giữ.

Lưu ý rằng phương thức này là không dễ sử dụng đối với an ninh. Một số trình duyệt không gửi đúng thông tin muốn chọn vì hoặc là chúng không hỗ trợ nó hoặc người sử dụng đã thay đổi những gì đang được gửi. Nhưng ví dụ này sẽ cho bạn một ý tưởng về các loại công việc mà bạn có thể làm để trợ giúp kiểm soát nội dung của bạn.


Tóm tắt

Hướng dẫn này hoàn thành loạt bài ba phần về "Học PHP" trong khi bạn xây dựng một ứng dụng tiến trình công việc đơn giản. Các phần trước đó tập trung vào các vấn đề cơ bản, như cú pháp, xử lý biểu mẫu, truy cập cơ sở dữ liệu, tải lên tập tin và XML. Trong phần này, bạn đưa tất cả điều đó tiến lên một bước nữa và đặt nó lại với nhau bằng cách tạo ra một biểu mẫu để qua đó một người quản trị hệ thống có thể phê duyệt các tập tin khác nhau. Chúng ta đã thảo luận các chủ đề sau:

  • Sử dụng xác thực HTTP.
  • Tạo luồng các tập tin.
  • Tạo các lớp và các đối tượng.
  • Các đặc tính và các phương thức đối tượng.
  • Sử dụng các hàm tạo đối tượng.
  • Sử dụng các ngoại lệ.
  • Tạo các ngoại lệ tùy chỉnh.
  • Sử dụng các thuộc tính ID của XML.
  • Thực hiện các việc kiểm tra an ninh bổ sung cho tải về

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=548708
ArticleTitle=Học PHP, Phần 3: Xác thực, các đối tượng, các ngoại lệ và tạo luồng
publish-date=09302010