Làm thế nào để triển khai một bitmask trong php?


76

Tôi không chắc liệu bitmask có phải là thuật ngữ chính xác hay không. Hãy để tôi giải thích:

Trong php, error_reportinghàm có thể được gọi theo nhiều cách:

// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);

// Reporting E_NOTICE can be good too (to report uninitialized
// variables or catch variable name misspellings ...)
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);

// Report all errors except E_NOTICE
// This is the default value set in php.ini
error_reporting(E_ALL ^ E_NOTICE);

Tôi lấy thuật ngữ bitmask từ trang php.net ở đây

Dù sao thì điểm của vấn đề này là, tôi đã triển khai một phương thức SIMPLE được gọi là phương thức lstrả về nội dung của một thư mục.

Hàm này có 3 args ... ($ include_hiised = false, $ return_absolute = false, $ ext = false)

Vì vậy, khi tôi gọi hàm, tôi đặt cách tôi muốn kết quả. Cho dù tôi muốn kết quả trả về thư mục ẩn, cho dù tôi chỉ muốn tên cơ sở, v.v.

vì vậy khi tôi gọi hàm, tôi đang viết

ls(true, false, true)
ls(false, false, true)
ls(true, true, true)
etc...

Tôi nghĩ rằng nó sẽ dễ đọc hơn nhiều nếu tôi có thể gắn cờ cách tôi muốn dữ liệu được trả về?

vì vậy một cái gì đó như:

ls( INCLUDE_HIDDEN | HIDE_EXTS );
ls( SHOW_ABSOLUTE_PATHS | HIDE_EXTS );

Vân vân...

Làm cách nào để thực hiện điều này trong điều kiện kiểm tra cờ nào đã được gọi?

Câu trả lời:


149

Thực ra nó khá đơn giản. Đầu tiên là một đoạn mã để chứng minh cách nó có thể được triển khai. Nếu bạn không hiểu bất kỳ điều gì về mã này đang hoạt động hoặc cách nó hoạt động, vui lòng đặt câu hỏi bổ sung trong phần nhận xét:

const FLAG_1 = 0b0001; // 1
const FLAG_2 = 0b0010; // 2
const FLAG_3 = 0b0100; // 4
const FLAG_4 = 0b1000; // 8
// Can you see the pattern? ;-)

function show_flags ($flags) {
  if ($flags & FLAG_1) {
    echo "You passed flag 1!<br>\n";
  }
  if ($flags & FLAG_2) {
    echo "You passed flag 2!<br>\n";
  }
  if ($flags & FLAG_3) {
    echo "You passed flag 3!<br>\n";
  }
  if ($flags & FLAG_4) {
    echo "You passed flag 4!<br>\n";
  }
}

show_flags(FLAG_1 | FLAG_3);

Bản giới thiệu


Vì cờ là số nguyên, trên nền tảng 32 bit, bạn xác định tối đa 32 cờ. Trên nền tảng 64-bit, nó là 64. Cũng có thể định nghĩa cờ dưới dạng chuỗi, trong trường hợp này, số lượng cờ có sẵn nhiều hơn hoặc ít hơn vô hạn (tất nhiên là trong giới hạn của tài nguyên hệ thống). Đây là cách nó hoạt động trong hệ nhị phân (cắt giảm xuống số nguyên 8-bit cho đơn giản).

FLAG_1
Dec:    1
Binary: 00000001

FLAG_2
Dec:    2
Binary: 00000010

FLAG_3
Dec:    4
Binary: 00000100

// And so on...

Khi bạn kết hợp các cờ để chuyển chúng đến hàm, bạn HOẶC chúng với nhau. Hãy xem điều gì sẽ xảy ra khi chúng ta vượt quaFLAG_1 | FLAG_3

  00000001
| 00000100
= 00000101

Và khi bạn muốn xem cờ nào đã được đặt, bạn VÀ bitmask với cờ. Vì vậy, hãy lấy kết quả ở trên và xem liệu FLAG_3đã được đặt chưa:

  00000101
& 00000100
= 00000100

... chúng tôi lấy lại giá trị của cờ, một số nguyên khác 0 - nhưng nếu chúng tôi thấy nếu FLAG_2được đặt:

  00000101
& 00000010
= 00000000

... chúng tôi nhận được số không. Điều này có nghĩa là bạn có thể đơn giản đánh giá kết quả của phép toán AND dưới dạng boolean khi kiểm tra xem giá trị có được chuyển hay không.


9
Vì vậy, các hoạt động bitwise cơ bản là: $flags & FLAG_1- kiểm tra xem FLAG_1 đã được đặt chưa, $flags | FLAG_1- đặt FLAG_1, $flags & ~FLAG_1- bỏ đặt FLAG_1, ~$flags- đảo ngược cờ
Konstantin Pereiaslov

7
Tôi không biết PHP và có thể sẽ không bao giờ tìm hiểu nó, nhưng tôi vui mừng tôi stumbled khi câu hỏi này - giải thích của bạn có thể giúp đỡ bất cứ ai thực hiện bit mapping bằng ngôn ngữ nào :)
Chris Cirefice

6
Tôi muốn giới thiệu việc xác định sức mạnh 2 số thập phân như hoạt động chút thay đổi khôn ngoan của 1. define('FLAG_1', 1<<0); define('FLAG_2', 1<<2); define('FLAG_3', 1<<3); define('FLAG_4', 1<<4); Đây là cách nó thường được thực hiện trong C
AmitP

6
@AmitP Có một vài vấn đề (nhỏ) với cách tiếp cận này trong PHP. 1) hiện tại consttừ khóa không hỗ trợ các biểu thức, ngay cả khi kết quả là không đổi (tức const FLAG_1 = 1 << 0;là lỗi phân tích cú pháp) - rõ ràng đây không phải là vấn đề với define()2) trong C một biểu thức hằng số như const int FLAG_1 = 1 << 0;sẽ được giải quyết tại thời điểm biên dịch và thực tế được biên dịch giá trị sẽ là kết quả của biểu thức, trong khi trong PHP, giá trị này được đánh giá mọi lúc, một lần truy cập hiệu suất nhỏ. Mặc dù vậy, cả hai vấn đề này đều không phải là lý do chính đáng để tránh phiên bản dễ đọc hơn mà bạn đề xuất.
DaveRandom

1
@Benjamin không liên quan đến câu hỏi này nhưng một điều hữu ích bạn có thể làm với const vô hướng exprs: const IS_WINDOWS = \PHP_OS & "\xDF\xDF\xDF" === 'WIN';là một tương đương thời gian biên dịch của stripos(\PHP_OS, 'WIN') === 0thành ngữ rất phổ biến . Khi nó xảy ra, điều này hiện tại là vô nghĩa ( PHP_OShiện luôn có 'WINNT'trên tất cả các phiên bản Windows) nhưng tôi đoán nó là bằng chứng trong tương lai ...
DaveRandom

17
define( "INCLUDE_HIDDEN", 0x1 );
define( "HIDE_EXTS", 0x2 );
define( "SHOW_ABSOLUTE_PATHS", 0x4 );
//And so on, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800 etc..

Sau đó, bạn có thể kiểm tra các cờ riêng lẻ trong lschức năng của mình :

if( $flags & INCLUDE_HIDDEN ) { //<-- note just a single &, bitwise and
    //$flags have INCLUDE_HIDDEN
}

7
Bạn có biết lợi thế của việc sử dụng số hex thay vì số nguyên không? Chỉ cần tò mò :)
AlexMorley-Finch

Vẻ @ AlexMorley-Finch như nó hoàn toàn là sở thích, sử dụng số thập phân hoặc hex dường như không thay đổi thời gian thực hiện ở tất cả, hoặc là
Brian Leishman

2

Những người khác đã đưa ra các đề xuất tốt, nhưng ngày nay, việc chuyển các mảng liên kết thay vì mặt nạ bit trở nên phổ biến hơn nhiều . Nó dễ đọc hơn nhiều và cho phép bạn chuyển các biến khác ngoài giá trị true / false. Một cái gì đó như thế này:

myFunction(['includeHidden' => true, 'fileExts' => false, 'string' => 'Xyz']);

function myFunction($options) {
    // Set the default options
    $options += [
        'includeHidden' => false,
        'fileExts' => true,
        'string' => 'Abc',
    ];

    if ($options['includeHidden']) {
        ...
    }
    ...
}

3
Tôi cũng đã từng làm theo cách này, nhưng những người bạn đồng hành phàn nàn rằng không có "gợi ý kiểu" trong IDE của họ, không có nhận xét tài liệu thích hợp và chức năng không phát ra lỗi khi thông số yêu cầu không được thông qua . Lý tưởng nhất là "tên params" sẽ giải quyết tất cả những vấn đề này nếu họ tồn tại trong php
Timo Huovinen

Xin chào @TimoHuovinen - cảm ơn bạn đã nhận xét. Thật dễ dàng để thêm các nhận xét doc trong đó phác thảo các tùy chọn & mặc định, đưa ra một ngoại lệ nếu bạn cần và đặt tham số làm mặc định cho một mảng cho phép gợi ý kiểu. Điều đó sẽ làm việc cho bạn?
Simon East,

Ồ, tôi hiểu rồi. Lớp Symfony OptionsResolver tiến thêm một bước nữa để cung cấp nhiều tính năng hơn nữa. Mát mẻ.
Simon East,

" Thật dễ dàng để thêm nhận xét tài liệu trong đó phác thảo các tùy chọn và mặc định " vâng, bạn có thể thêm chúng giống như twig vẫn làm nhưng đó vẫn là một cách giải quyết. " và đặt tham số mặc định thành một mảng cho phép gợi ý kiểu " bạn sẽ làm điều này như thế nào? Ý tôi là gợi ý loại IDE và hoàn thành mã, nơi trình chỉnh sửa cho bạn biết những tham số nào mà hàm có thể chấp nhận. (Hãy nhớ, tôi cũng sử dụng mảng cho các tùy chọn, chỉ là tôi cần phải tìm ra cách để giữ cho đồng hạnh phúc)
Timo Huovinen

Vấn đề với cách tiếp cận này là bạn sẽ kết thúc với một số lượng lớn các câu lệnh if else
numediaweb
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.