Многие сталкивались с вопросом: как бы передать в запросе много условий как можно меньшим количеством переменных? Предлагаю познакомиться с типовым, но, к сожалению, непопулярным решением — передача ключей битовой маской .

Уверен, что можно опустить описание всего механизма как именно располагаются разряды и как происходит сохранение данных в битовой маске, и перейти непосредственно к выдуманной задачке.

Задача. Существует каталог киножанров и несколько фильмов. Каждый фильм может иметь привязку к нескольким жанрам. Необходимо вывести список фильмов с прикрепленными к ним жанрами.

Пример php кода с битовой маской

#I /* типы жанров, расположенные с шагом степени двойки */ #I $genres = array( '1' => 'Комедия',
'2' => 'Драма',
'4' => 'Приключение',
'8' => 'Боевик',
'16' => 'Фантастика'
); /* наша "база данных" фильмов */ $movies = array( 'Pirates of the Caribbean' => array( 'title' => 'Пираты карибского моря', 'genres' => 4 ), 'Green Lantern' => array( 'title' => 'Зеленый фонарь', 'genres' => 24 ), 'Larry Crown' => array( 'title' => 'Ларри Краун', 'genres' => 3 ), 'Anonymous' => array( 'title' => 'Аноним', 'genres' => 2 ), ); /* выводим список фильмов */ foreach( $movies AS $m=>$movie ){ #I // выводим название фильма #I echo '<h2>'.$movie['title'].'</h2>'; #I // выводим английское название #I echo '<div>'.$m.'</div>'; #I // делаем пустой карман для жанров $movieGenres = array(); // ищем жанры, принадлежащие фильму #I foreach( $genres AS $g=>$genre ){ if( ( $movie['genres'] & $g ) > 0 ){ #I // добавляем в карман жанр #I $movieGenres[] = $genre; } } // выводим жанры фильма echo implode( ', ', $movieGenres ); }

Объяснение неочевидностей

Ну первое, что бросается в глаза это

'Larry Crown' => array( 'title' => 'Ларри Краун', 'genres' => 3 ),

Возникает вопрос: почему в жанры попал несуществующий ключ(тройка) ?

Ответ: ключ три — это битовая комбинация единицы и двойки.

Думаю, двадцать четыре, как комбинация восьмерки и шестнадцати, так же понятно.

Ну и остается вопрос: как проверить маску? Тут на помощь приходят элементарное условие «if», писать которое для проверки битовой маски никто не помнит как. А на самом деле все просто:

if( ( $var & $key ) > 0 ){ ... }

где $var — то значение, кое мы имеем, а $key — тот ключ, который мы ищем. Если операция возвращает более 0, точнее значение искомого ключа, то жанр мы сохраняем в карман.

Ну и неохваченная в примере кода область.

Как добавить ключ к существующей битовой маске?

ключи массива - степень двойки! 2,4,8,16...1024...

Допустим мы хотим к фильму «Аноним» добавить еще жанр «фантастика», который имеет ключ 16 в массиве. Для этого нам нужно добавить следующее:

$movies['Anonymous']['genres'] |= 16;

Вертикальная черта и равно, и теперь наш «Аноним» имеет жанры «драма, фантастика».

или примитивнее:

$movies['Anonymous']['genres'] = $movies['Anonymous']['genres'] | 16;

Конечно, теперь не сложно переставить требуемый параметр в GET и потом поискать его в битовых масках. :)