PHP - Namespaces

Chúng tôi thường tổ chức các tệp trong các thư mục khác nhau. Thông thường, một thư mục chứa các tệp liên quan đến một mục tiêu, ứng dụng hoặc danh mục nhất định. Một thư mục không thể chứa hai tệp có cùng tên, mặc dù các thư mục khác nhau có thể có một tệp cùng tên để đường dẫn của mỗi tệp là khác nhau.

Ý tưởng về không gian tên (namespaces) trong PHP có phần tương tự. Trong PHP, không gian tên cho phép các lớp, hàm hoặc hằng số có cùng tên được sử dụng trong các ngữ cảnh khác nhau mà không xảy ra xung đột, từ đó bao bọc những mục này lại với nhau.

Một không gian tên PHP là một nhóm logic của các lớp/hàm, v.v., tùy thuộc vào sự liên quan của chúng. Cũng giống như một tệp có cùng tên có thể tồn tại trong hai thư mục khác nhau, một lớp có tên nhất định có thể được định nghĩa trong hai không gian tên khác nhau. Hơn nữa, khi chúng ta chỉ định đường dẫn đầy đủ của một tệp để truy cập, chúng ta cần chỉ định tên đầy đủ của lớp cùng với không gian tên.

Khi kích thước ứng dụng của bạn trở nên lớn hơn, bao gồm nhiều định nghĩa lớp và hàm, việc đặt tên duy nhất cho mỗi lớp/hàm có thể trở nên t tedious và không thực sự thanh lịch. Sử dụng không gian tên (namespaces) cho phép bạn tổ chức các khối mã như vậy một cách gọn gàng. Ví dụ, nếu chúng ta cần khai báo một hàm calculate() để tính diện tích cũng như thuế, thay vì định nghĩa chúng như calculate_area() và calculate_tax(), chúng ta có thể tạo hai không gian tên là area và tax và sử dụng calculate() bên trong chúng.

Advantages of Namespace

Dưới đây là một số lợi ích của việc sử dụng không gian tên (namespaces) trong PHP −

  • Các không gian tên (namespaces) giúp tránh xung đột tên giữa các lớp/hàm/hằng số được định nghĩa bởi ai đó với các lớp/hàm/hằng số của bên thứ ba.

  • Namespaces cung cấp khả năng tạo bí danh (hoặc rút ngắn) cho các Tên_Dài_Thêm, từ đó cải thiện khả năng đọc của mã nguồn.

  • Các không gian tên trong PHP cung cấp một cách để nhóm các lớp, giao diện, hàm và hằng số liên quan. Tên không gian tên không phân biệt chữ hoa chữ thường.

Defining a Namespace

Từ khóa namespace trong PHP được sử dụng để định nghĩa một không gian tên mới.

namespace myspace;

Một tệp ".php" chứa không gian tên phải khai báo không gian tên ở đầu tệp trước bất kỳ điều gì khác (ngoại trừ chỉ thị declare). Việc khai báo lớp, hàm và hằng số bên trong một không gian tên ảnh hưởng đến quyền truy cập của chúng.

Một đoạn mã PHP có thể chứa các mã khác ngoài định nghĩa của một không gian tên. Để tải không gian tên được định nghĩa trong cùng một mã, PHP sử dụng từ khóa "use".

use myspace;

Example

Trong đoạn mã "hello.php" sau đây, chúng ta định nghĩa một hàm hello() bên trong không gian tên myspace, và gọi nó sau khi tải không gian tên trong đoạn mã hiện tại.

<?php
   namespace myspace;
   function hello() {
      echo "Hello World";
   }
   use myspace;
   myspace\hello();
?>

Nó sẽ tạo ra output

Hello World

Lưu ý rằng bạn phải chỉ định hàm hello() với tên đầy đủ của nó bao gồm không gian tên - myspace\hello().

Include Namespace

Bạn có thể có một kịch bản gồm một khai báo không gian tên, và kịch bản khác trong đó không gian tên được nạp bằng câu lệnh include.

a.php

<?php
   namespace myspace {
      function hello() {
         echo "Hello World in myspace";
      }
   }
?>

b.php

<?php
   include 'a.php';
   myspace\hello();
?>

Nó sẽ tạo ra output

Hello World in myspace

Có thể có trường hợp mà kịch bản hiện tại ("b.php" như trên) cũng có một hàm cùng tên như trong tệp đã được bao gồm. Hàm có tên đầy đủ với tiền tố không gian tên sẽ giúp trình phân tích cú pháp giải quyết xung đột tên.

Example

Hãy xem xét ví dụ sau −

<?php
   include 'a.php';
   function hello() {
      echo "Hello World from current namespace";
   }
   hello();
   myspace\hello();
?>

Nó sẽ tạo ra output

Hello World from current namespace
Hello World in myspace

Example

Như đã đề cập ở trên, khai báo không gian tên phải ở đầu, ngay sau thẻ <?php mở. Nếu không, trình phân tích cú pháp sẽ ném ra một lỗi nghiêm trọng.

<?php
   echo "hello"
   namespace myspace;
   function hello() {
      echo "Hello World";
   }
   use myspace;
   myspace\hello();
?>

Nó sẽ tạo ra output

PHP Parse error:  syntax error, unexpected token "namespace", 
expecting "," or ";" in /home/cg/root/67771/main.php on line 4

Thông báo lỗi trên làm rõ rằng chỉ có "câu lệnh declare" được phép xuất hiện trước khai báo không gian tên.

<?php
   declare (strict_types=1);
   namespace myspace;
   function hello() {
      echo "Hello World";
   }
   use myspace;
   myspace\hello();
?>

Relative Namespace

Các đối tượng như hàm, lớp và hằng số có thể được truy cập trong không gian tên hiện tại bằng cách tham chiếu đến các đường dẫn không gian tên tương đối.

Trong ví dụ sau, "b.php" chứa một không gian tên space1\myspace với một hàm hello() và một hằng số TEMP. Các đối tượng tương tự cũng được định nghĩa trong không gian tên space1, có mặt trong "a.php".

Rõ ràng, khi "b.php" được bao gồm trong "a.php", "myspace" là một không gian con của "space1". Do đó, hello() từ "myspace" được gọi bằng cách thêm tiền tố cho không gian tên tương đối của nó (cũng là hằng số TEMP).

b.php

<?php
   namespace space1\myspace;
   const TEMP = 10;
   function hello() {
      echo "Hello from current namespace:" . __NAMESPACE__ . ;
   }
?>

a.php

<?php
   namespace space1;
   include 'b.php';
   function hello() {
      echo "Hello from current namespace:" . __NAMESPACE__ . ;
   }
   const TEMP = 100;
   hello();            // current namespace
   myspace\hello();   // sub namespace

   echo "TEMP : " . TEMP . " in " . __NAMESPACE__ . ;
   echo "TEMP : " . myspace\TEMP  . " \\in space1\\myspace\n";
?>

Nó sẽ tạo ra output

Hello from current namespace:space1
Hello from current namespace:space1\myspace
TEMP : 100 in space1
TEMP : 10 in space1\myspace

Absolute Namespace

Bạn cũng có thể truy cập các hàm/hằng số từ bất kỳ không gian tên nào bằng cách thêm tiền tố cho đường dẫn không gian tên tuyệt đối. Ví dụ, hello() trong "b.php" là "\space\myspace\hello()".

a.php

<?php
   namespace space1;
   include 'b.php';
   function hello() {
      echo "Hello from current namespace:" . __NAMESPACE__ . ;
   }
   const TEMP = 100;
   \space1\hello();	             //current namespace
   \space1\myspace\hello();	    //sub namespace

   echo "TEMP: " . \space1\TEMP . " in " . __NAMESPACE__ . ;
   echo "TEMP: " . \space1\myspace\TEMP  . " in space1\\myspace\n";
?>

__NAMESPACE__ là một hằng số đã được định nghĩa trong PHP, nó trả về tên của không gian tên hiện tại.

Namespace Rules

Bất kỳ xung đột nào trong tên của hàm/lớp/hằng số xuất hiện giữa các không gian tên khác nhau sẽ được giải quyết theo các quy tắc sau đây −

  • Một định danh không gian tên (namespace) không có ký hiệu phân cách không gian tên (/) có nghĩa là nó đang tham chiếu đến không gian tên hiện tại. Đây là một tên không được định danh (unqualified name).

  • Nếu nó chứa ký hiệu phân tách như trong myspace\space1, nó sẽ được giải quyết thành một không gian con space1 dưới myspace. Loại tên này được gọi là không gian tên tương đối.

  • Tên của không gian tên đủ điều kiện bắt đầu bằng ký tự "\". Ví dụ, "\myspace" hoặc "\myspace\space1".

  • Tên đầy đủ (fully qualified names) được giải quyết thành không gian tên tuyệt đối. Ví dụ, \myspace\space1 được giải quyết thành không gian tên myspace\space1.

  • Nếu tên xuất hiện trong không gian tên toàn cục, tiền tố "namespace\" sẽ bị loại bỏ. Ví dụ, "namespace\space1" sẽ được giải quyết thành space1.

  • Tuy nhiên, nếu nó xảy ra bên trong một không gian tên khác, nó sẽ được xử lý khác đi. Ví dụ, nếu namespace\space1 nằm trong myspace, nó tương đương với "myspace\space1".

  • Phần đầu tiên của tên trong tên đủ được dịch theo bảng nhập lớp/namespace hiện tại.

  • Nếu không có quy tắc nhập nào áp dụng, không gian tên hiện tại sẽ được thêm vào tên.

  • Tên giống như lớp được dịch theo bảng nhập khẩu lớp/namespace, tên hàm theo bảng nhập khẩu hàm và hằng số theo bảng nhập khẩu hằng số.

  • Đối với các tên không đủ điều kiện, nếu không có quy tắc nhập nào áp dụng và tên đó tham chiếu đến một hàm hoặc hằng số và mã nằm ngoài không gian tên toàn cầu, tên đó sẽ được giải quyết tại thời gian chạy. Đầu tiên, nó sẽ tìm kiếm một hàm từ không gian tên hiện tại, sau đó nó sẽ cố gắng tìm và gọi hàm toàn cầu.