Với PHP 7, bạn có thể chọn viết mã an toàn hơn nhiều so với trước đây, nhờ gợi ý kiểu vô hướng và kiểu trả về
function repeat[string $text, int $times] : string;
Nhưng còn mảng thì sao? . Đối với IDE, bạn có thể thêm nhận xét PhpDoc
/** * @return User[] */ function allUsers[] : array;
Giờ đây, các IDE như PhpStorm có thể giúp hoàn thành mã cho các mục trong mảng được trả về. Nhưng chúng tôi không thể hưởng lợi từ bất kỳ kiểm tra nào trong thời gian chạy, như với các gợi ý kiểu thực
Đối với các đối số, có một cách giải quyết một phần, sử dụng các đối số biến đổi. Thực hiện chức năng sau
/** * @param User[] $users */ function deleteUsers[array $users];
Với các đối số matrixdic, chúng ta có thể viết lại nó thành
function deleteUsers[User ...$users];
Cách sử dụng cũng thay đổi, thành
/** * @param User[] $users */ function deleteUsers[array $users];1 Trong lệnh gọi này, đối số
/** * @param User[] $users */ function deleteUsers[array $users];2 sẽ được “giải nén” thành các biến đơn lẻ và trong chính phương thức đó, nó được “đóng gói” lại thành một mảng
/** * @param User[] $users */ function deleteUsers[array $users];2. Mỗi mặt hàng được xác nhận thuộc loại
/** * @param User[] $users */ function deleteUsers[array $users];4.
/** * @param User[] $users */ function deleteUsers[array $users];2 cũng có thể là một iterator, nó sẽ được chuyển đổi thành một mảng
Thật không may, không có cách giải quyết tương tự cho các kiểu trả về và nó chỉ hoạt động cho đối số cuối cùng
Xem thêm. Nhập gợi ý trong PHP 7 – mảng đối tượng
Tôi đã sử dụng kỹ thuật này rất nhiều trong mã PHP 7, nhưng tôi đã tìm thấy một kỹ thuật khác thậm chí còn tốt hơn và không có các lỗi đã đề cập
đối tượng bộ sưu tập
Mỗi khi tôi cần một mảng đối tượng, tôi tạo một lớp thay thế. Đây là một ví dụ đơn giản
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }
Bộ sưu tập này kéo dài
/** * @param User[] $users */ function deleteUsers[array $users];6, vì vậy nó có thể được sử dụng gần giống như một mảng. lặp lại với
/** * @param User[] $users */ function deleteUsers[array $users];7 và truy cập các phần tử với
/** * @param User[] $users */ function deleteUsers[array $users];8. Hàm mảng không dùng được nhưng với
/** * @param User[] $users */ function deleteUsers[array $users];9 thì có thể chuyển thành mảng bình thường
Lưu ý rằng bạn chỉ có thể thay đổi gợi ý kiểu trả về để hạn chế hơn trong lớp cha. Vì vậy, việc ghi đè
function deleteUsers[User ...$users];0 sẽ không hoạt động và đối tượng sẽ ở trạng thái chưa được xác thực cho đến khi từng mục được truy cập
Nếu chúng ta không cần giao diện
function deleteUsers[User ...$users];1 để truy cập các phần tử với
/** * @param User[] $users */ function deleteUsers[array $users];8, thì có một giải pháp cho điều đó. đóng gói trình lặp mảng và chỉ hiển thị những gì bạn cần
class Users extends IteratorIterator { public function __construct[User ...$users] { parent::__construct[new ArrayIterator[$users]]; } public function current[] : User { return parent::current[]; } }
[bạn có bao giờ tự hỏi, tại sao
function deleteUsers[User ...$users];3 lại tồn tại không? Ở đây tôi đã tìm thấy một trường hợp sử dụng. ]
Đối tượng
function deleteUsers[User ...$users];4 này chỉ có thể được tạo với các phần tử
/** * @param User[] $users */ function deleteUsers[array $users];4, nhờ các đối số hàm tạo biến đổi. Chuyển bất cứ thứ gì khác cho hàm tạo sẽ dẫn đến một
function deleteUsers[User ...$users];6 có thể bắt được
new Users[$user1, $user2, $user3];
Và nó thậm chí là bất biến, nó không thể thay đổi sau khi khởi tạo. Nhưng nếu chúng ta cần thay đổi nó, bây giờ chúng ta có thể thêm các phương thức an toàn cho điều đó, như
public function add[User $user] { $this->getInnerIterator[]->append[$user]; } public function set[int $key, User $user] { $this->getInnerIterator[]->offsetSet[$key, $user]; }
Bạn cũng có thể làm cho nó có thể đếm được [vì chúng tôi biết, trình vòng lặp bên trong thực hiện
function deleteUsers[User ...$users];7]
public function count[] : int { return $this->getInnerIterator[]->count[]; }
Hàm mảng
Như đã đề cập trước đây, nếu bạn cần các hàm mảng, bạn luôn có thể chuyển đổi đối tượng thành một mảng bằng
/** * @param User[] $users */ function deleteUsers[array $users];9. Nhưng có một giải pháp khác thường hợp lý hơn. di chuyển chức năng vào lớp bộ sưu tập. Đối với các hàm sắp xếp, thật dễ dàng vì chúng đã là một phần của
/** * @param User[] $users */ function deleteUsers[array $users];6, vì vậy chúng ta có thể thực hiện tương tự như với
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }0 ở trên
Nhưng hãy lấy một ví dụ khác và hợp nhất hai bộ sưu tập người dùng
public function merge[Users $other] { return new Users[ array_merge[ iterator_to_array[$this], iterator_to_array[$other] ] ]; }
Bây giờ nó có thể được sử dụng như
/** * @return User[] */ function allUsers[] : array;0
Khi thêm các phương thức vào các đối tượng bộ sưu tập, hãy càng cụ thể càng tốt để chuyển logic vào bộ sưu tập thuộc về đó. Ví dụ: thay vì tạo một phương thức
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }1 chung chung, hãy tạo chính xác [các] phương thức sắp xếp mà bạn cần
/** * @return User[] */ function allUsers[] : array;1
Điều đó làm cho mã máy khách rõ ràng hơn nhiều [điều không thể thực hiện được với mảng]
/** * @return User[] */ function allUsers[] : array;2
Với các phương thức lọc/ánh xạ/thu nhỏ, chúng tôi cũng có tùy chọn để làm việc với các đường ống thu thập, như tôi đã giải thích trong một bài đăng trên blog trước đây. Đường ống thu thập trong PHP
Lôgic tên miền
Khi tôi cần một tập hợp các đối tượng, trước tiên tôi thường tạo một lớp đơn giản, mở rộng
function deleteUsers[User ...$users];3, như trong ví dụ trên. Ưu điểm rõ ràng là an toàn kiểu, nhưng nó cũng giúp dễ dàng thêm logic miền vào đúng chỗ sau này. Ngay khi tôi có các phương thức hoạt động trên một tập hợp các đối tượng, tôi có thể thêm chúng trực tiếp vào lớp tập hợp, nơi chúng thuộc về một cách tự nhiên. Nếu không, chúng có thể kết thúc trong một số lớp tiện ích hoặc trình trợ giúp [Ngừng sử dụng Trình trợ giúp. ] hoặc thậm chí trong mã máy khách sử dụng bộ sưu tập
Hãy coi đây là một phần của trò chơi bài
/** * @return User[] */ function allUsers[] : array;3
Nó kết hợp logic trò chơi cấp cao với các chi tiết triển khai cấp thấp. Với một đối tượng bộ sưu tập thẻ, chúng ta có thể di chuyển các phương thức hoạt động trên thẻ ở đó và giữ lớp Game ở một mức trừu tượng
/** * @return User[] */ function allUsers[] : array;4
giao diện
Trước tiên, việc xác định bộ sưu tập là giao diện thường hữu ích. Nó mở rộng giao diện
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }3 và chứa ít nhất phương thức
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }4 với kiểu trả về được chỉ định
Ví dụ tối thiểu
/** * @return User[] */ function allUsers[] : array;5
Với cách triển khai mặc định như được bao bọc
/** * @param User[] $users */ function deleteUsers[array $users];6
/** * @return User[] */ function allUsers[] : array;6
Tại sao nó hữu ích?
Thực hiện có thể trao đổi
Đầu tiên, nếu mã của bạn sẽ được sử dụng bởi các bên thứ ba, họ sẽ có thể hoán đổi việc triển khai. Ví dụ yêu thích của tôi là lười tải các bộ sưu tập với trình tạo để có hiệu suất tốt hơn với các bộ sưu tập lớn
/** * @return User[] */ function allUsers[] : array;7
mà sau đó có thể được khởi tạo như thế này
/** * @return User[] */ function allUsers[] : array;8
Ở đây, các đối tượng Người dùng không được tìm nạp từ cơ sở dữ liệu trước khi bạn thực sự lặp lại bộ sưu tập tải chậm. Vì các trình tạo chỉ có thể được lặp lại một lần, nên bộ sưu tập sẽ không giữ tất cả các đối tượng trong bộ nhớ cùng một lúc
Khả năng mở rộng với trang trí
Thứ hai, bạn sẽ có thể mở rộng lớp với các trình trang trí, bao bọc thể hiện ban đầu và triển khai cùng một giao diện, nhưng thêm hành vi bổ sung. Bạn thậm chí có thể sử dụng bộ sưu tập để trang trí tất cả các mặt hàng một cách nhanh chóng
Giả sử việc triển khai
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }6 mặc định trong trò chơi bài tuyệt vời của chúng ta có một phương thức
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }7 trả về quân bài phù hợp và xếp hạng dưới dạng văn bản thuần túy, đồng thời thêm một trình trang trí cho biểu diễn HTML
/** * @return User[] */ function allUsers[] : array;9
Bây giờ chúng ta cũng có thể viết một trình trang trí cho bộ sưu tập, lấy bất kỳ bộ sưu tập
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }8 nào và trang trí các phần tử của nó bằng
class Users extends ArrayIterator { public function __construct[User ...$users] { parent::__construct[$users]; } public function current[] : User { return parent::current[]; } public function offsetGet[$offset] : User { return parent::offsetGet[$offset]; } }9