ITDumka
Примеры шаблонов проектирования или как написать свой PHP framework. Часть 1: Строковый фасад

Каждый из Web-программистов может прийти к такому моменту своей жизни, когда захочет сделать свой собственный движок, цивилизованными словами - framework, для каких-то своих разработок. Я хочу помочь вам в этом и отдать свой опыт разработки в ваше распоряжение. Возможно, вы почерпнёте из этого материала хороший кусок знаний, так как сама разработка будет основана на применении различных шаблонов проектирования. В этой статье я расскажу о применении шаблона проектирования фасад (Facade) в контексте класса обработки срок. Что же это за класс и зачем он нужен?


Допустим, вы пишете русскоязычный сайт. Естественно при этом вы пользуетесь различными строковыми функция, такими как substr, strlen и т.д., которые работают с однобайтовой кодировкой, например Windows-1251. Вот вы написали сайт, запустили его, он работает и радует ваш глаз. Но вдруг вам понадобилось расширить локализацию сайта языком, алфавит которого не может описаться одним байтом. Тут, конечно же, вам на помощь придёт кодировка UTF-8. Как известно PHP нормально работает с многобайтовой кодировкой посредством расширения mbstring. И вы задаёте себе вопрос: заменять все строковые функции в проекте на mb_*? Конечно же нет. Для таких случаев уже придумали фичу, которая заключается в регулировании параметра php.ini mbstring.func_overload. По умолчанию значение этого параметра равно нулю, но если вы установите его в двойку, то строковые функции, работающие с однобайтовыми символами, перегрузятся функциями из расширения mbstring. То есть вместо substr будет вызвана mb_substr, хотя написана будет именно первая. Этот очень хороший механизм пригодится, когда вам необходимо внедрить код, написанный с помощью стандартных строковых функций в приложение, работающее в многобайтном режиме. Класс, который я собираюсь вам показать, попросту абстрагирует приложение от параметра mbstring.func_overload. Конечно же, многим этот подход может не понравиться, вы даже можете сказать, что это вообще не нужно делать. Но я люблю быть независимым от подобных настроек, поэтому и вам рекомендую пользоваться таким подходом, раз уж разработчики PHP не были в состоянии сделать сразу всё как полагается.


Итак, какие задачи будет решать этот класс? Первое, что он должен сделать, это инкапсулировать в себе строковый функционал, запретив доступ программиста к стандартным строковым функциям, будь то substr или mb_substr. В это же время он предоставит один интерфейс, то есть одни методы доступа, для вызова различных функций. Как я уже сказал выше, это будет явный представитель типового решения фасад (Facade), так как для доступа к двум интерфейсам функций мы предоставим доступ через один унифицированный. Для удобства он преобразует свой интерфейс в интерфейсы вызываемых функций. Перечислим обязанности нашего "строкового фасада":

1. Выбор режимов функционирования: однобайтовый / многобайтовый.

2. Детектирование настроек окружения и выбор соответствующих вызовов.

3. Статический интерфейс, совместимый с интерфейсом функций.

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


Listing №1 (PHP)
  1. class Str {
  2.  
  3.   const MODE_ANSII = 0;
  4.  
  5.   const MODE_MULTIBYTE = 1;
  6.   /**
  7.    * Текущий режим работы
  8.    * Принимает значения
  9.    * MODE_MULTIBYTE для работы в многобайтовом режиме
  10.    * или MODE_ANSII в однобайтовом
  11.    * По умолчанию в многобайтовом
  12.    * @static int
  13.    */
  14.   private static $_Mode = 1;
  15.   /**
  16.    * Текущая кодировка
  17.    * По умолчанию UTF-8
  18.    * @static stirng
  19.    */
  20.   private static $_Encoding = 'UTF-8';
  21.   /**
  22.    * Значение параметра mbstring.func_overload
  23.    * @static array
  24.    */
  25.   private static $_OverloadBitmask = 0;
  26.   /**
  27.    * Возвращает TRUE если нужно вызвать mb_* функцию
  28.    * @param int $OvelroadBitmask - флаг выбора возможных перезагруженных функций
  29.    * @return bool
  30.    */
  31.   private static function _NeedUseMBFuncs($OvelroadBitmask)
  32.   {
  33.     return (self::$_Mode && self::$_OverloadBitmask) ? (!($OvelroadBitmask & self::$_OverloadBitmask)) : self::$_Mode;
  34.   }


Как видите, мы выбираем конечную функцию в зависимости от нескольких параметров. Если режим работы многобайтовый и функции перегружены, то мы вызывает те функции, которые перегрузились, сравнивая побитно (обратите внимание на одинарный знак &) значение mbstring.func_overload с флагом, определяющим, в факте перегруженности каких функций мы сейчас заинтересованы.


Обычно реализуя какие-нибудь подобные вещи, я конструирую метод инициализации, который будет выполнять функцию конструктора для статического класса:


Listing №2 (PHP)
  1. /**
  2.  * @static
  3.  * @param int $Mode self::MODE_MULTIBYTE
  4.  * @param string $Encoding 'UTF-8'
  5.  */
  6. public static function Init($Mode = self::MODE_MULTIBYTE, $Encoding = 'UTF-8')
  7. {
  8.   self::$_Mode = $Mode;
  9.   if (!function_exists('mb_substr')) {
  10.     self::$_Mode = self::MODE_ANSII;
  11.   }
  12.   else {
  13.     mb_internal_encoding($Encoding);
  14.   }
  15.   self::$_Encoding = $Encoding;
  16.   self::$_OverloadBitmask = ini_get('mbstring.func_overload');
  17. }


Необходимо вызвать этот метод, перед первым использованием данного класса. Конечно, вы можете автоматизировать этот процесс по умолчанию, но я обычно не пользуюсь такими штуками, а люблю задавать всё явно. Теперь в конфигурации нашего проекта мы может инициализировать работу класса такой строкой:


Listing №3 (PHP)
  1. Str::Init(Str::MODE_MULTIBYTE, 'UTF-8');


Итак, всё готово. Теперь непосредственно реализуем то, для чего мы это всё затеяли. Не забываем согласовывать интерфейс с родными функциями. Конечно для паттерна фасад (Facade) это не обязательно, но в данном случае это хороший плюс. В итоге мы получаем приблизительно такую общую картину:


Listing №4 (PHP)
  1. class Str {
  2.  
  3.   const MODE_ANSII = 0;
  4.  
  5.   const MODE_MULTIBYTE = 1;
  6.   /**
  7.    * Текущий режим работы
  8.    * Принимает значения
  9.    * MODE_MULTIBYTE для работы в многобайтовом режиме
  10.    * или MODE_ANSII в однобайтовом
  11.    * По умолчанию в многобайтовом
  12.    * @static int
  13.    */
  14.   private static $_Mode = 1;
  15.   /**
  16.    * Текущая кодировка
  17.    * По умолчанию UTF-8
  18.    * @static stirng
  19.    */
  20.   private static $_Encoding = 'UTF-8';
  21.   /**
  22.    * Значение параметра mbstring.func_overload
  23.    * @static array
  24.    */
  25.   private static $_OverloadBitmask = 0;
  26.   /**
  27.    * @static
  28.    * @param int $Mode self::MODE_MULTIBYTE
  29.    * @param string $Encoding 'UTF-8'
  30.    */
  31.   public static function Init($Mode = self::MODE_MULTIBYTE, $Encoding = 'UTF-8')
  32.   {
  33.     self::$_Mode = $Mode;
  34.     if (!function_exists('mb_substr')) {
  35.       self::$_Mode = self::MODE_ANSII;
  36.     }
  37.     else {
  38.       mb_internal_encoding($Encoding);
  39.     }
  40.     self::$_Encoding = $Encoding;
  41.     self::$_OverloadBitmask = ini_get('mbstring.func_overload');
  42.   }
  43.   /**
  44.    * @static
  45.    * @return string
  46.    */
  47.   public static function GetCharset()
  48.   {
  49.     return self::$_Encoding;
  50.   }
  51.   /**
  52.    * @static
  53.    * @param string $String
  54.    * @param int $Start
  55.    * @param int $Length
  56.    * @return string
  57.    */
  58.   public static function Substr($String, $Start, $Length = NULL)
  59.   {
  60.     if (self::_NeedUseMBFuncs(2)) {
  61.       if (NULL === $Length) {
  62.         return mb_substr($String, $Start);
  63.       }
  64.       else {
  65.         return mb_substr($String, $Start, (int)$Length, self::$_Encoding);
  66.       }
  67.     }
  68.     else {
  69.       if (NULL === $Length) {
  70.         return substr($String, $String, $Length);
  71.       }
  72.       else {
  73.         return substr($String, $String);
  74.       }
  75.     }
  76.   }
  77.   /**
  78.    * @static
  79.    * @param string $String
  80.    * @return int
  81.    */
  82.   public static function Strlen($String)
  83.   {
  84.     if (self::_NeedUseMBFuncs(2)) {
  85.       return mb_strlen($String, self::$_Encoding);
  86.     }
  87.     else {
  88.       return strlen($String);
  89.     }
  90.   }
  91.   /**
  92.    * @static
  93.    * @param string $String
  94.    * @return string
  95.    */
  96.   public static function Strtolower($String)
  97.   {
  98.     return  (self::_NeedUseMBFuncs(2)) ? mb_strtolower($String, self::$_Encoding) : strtolower($String);
  99.   }
  100.   /**
  101.    * @static
  102.    * @param string $String
  103.    * @return string
  104.    */
  105.   public static function Strtoupper($String)
  106.   {
  107.     return  (self::_NeedUseMBFuncs(2)) ? mb_strtoupper($String, self::$_Encoding) : strtoupper($String);
  108.   }
  109.   /**
  110.    * @static
  111.    * @param string $HayStack
  112.    * @param string $Needle
  113.    * @param int $Offset
  114.    * @return int
  115.    */
  116.   public static function Strpos($Haystack, $Needle, $Offset = NULL)
  117.   {
  118.     if (self::_NeedUseMBFuncs(2)) {
  119.       return mb_strpos($Haystack, $Needle, $Offset, self::$_Encoding);
  120.     }
  121.     else {
  122.       return strpos($Haystack, $Needle, $Offset);
  123.     }
  124.   }
  125.   /**
  126.    * @static
  127.    * @param string $HayStack
  128.    * @param string $Needle
  129.    * @param int $Offset
  130.    * @return int
  131.    */
  132.   public static function Strrpos($Haystack, $Needle, $Offset = NULL)
  133.   {
  134.     if (self::_NeedUseMBFuncs(2)) {
  135.       return mb_strrpos($Haystack, $Needle, $Offset, self::$_Encoding);
  136.     }
  137.     else {
  138.       return strrpos($Haystack, $Needle, $Offset);
  139.     }
  140.   }
  141.   /**
  142.    * @static
  143.    * @param string $Pattern
  144.    * @param string $String
  145.    * @param int $Limit = NULL
  146.    */
  147.   public static function Split($Pattern, $String, $Limit = NULL)
  148.   {
  149.     if (self::_NeedUseMBFuncs(2)) {
  150.       if (NULL === $Limit) {
  151.         return mb_split($Pattern, $String);
  152.       }
  153.       else {
  154.         return mb_split($Pattern, $String, $Limit);
  155.       }
  156.     }
  157.     else {
  158.       if (NULL === $Limit) {
  159.         return split($Pattern, $String);
  160.       }
  161.       else {
  162.         return split($Pattern, $String, $Limit);
  163.       }
  164.     }
  165.   }
  166.   /**
  167.    * @static
  168.    * @param string $To
  169.    * @param string $Subject
  170.    * @param string $Message
  171.    * @param string $AdditionalHeaders = NULL
  172.    * @param string $AdditionalParameters
  173.    * @return bool
  174.    */
  175.   public static function Mail($To, $Subject, $Message, $AdditionalHeaders = NULL, $AdditionalParameters = NULL)
  176.   {
  177.     if (self::_NeedUseMBFuncs(1)) {
  178.       if (NULL === $AdditionalHeaders) {
  179.         return mb_send_mail($To, $Subject, $Message);
  180.       }
  181.       else {
  182.         if (NULL === $AdditionalParameters) {
  183.           return mb_send_mail($To, $Subject, $Message, $AdditionalHeaders);
  184.         }
  185.         else {
  186.           return mb_send_mail($To, $Subject, $Message, $AdditionalHeaders, $AdditionalParameters);
  187.         }
  188.       }
  189.     }
  190.     else
  191.     {
  192.       if (NULL === $AdditionalHeaders) {
  193.         return mail($To, $Subject, $Message);
  194.       }
  195.       else {
  196.         if (NULL === $AdditionalParameters) {
  197.           return mail($To, $Subject, $Message, $AdditionalHeaders);
  198.         }
  199.         else {
  200.           return mail($To, $Subject, $Message, $AdditionalHeaders, $AdditionalParameters);
  201.         }
  202.       }
  203.     }
  204.   }
  205.   /**
  206.    * Возвращает TRUE если нужно вызвать mb_* функцию
  207.    * @param int $OvelroadBitmask - флаг выбора возможных перезагруженных функций
  208.    * @return bool
  209.    */
  210.   private static function _NeedUseMBFuncs($OvelroadBitmask)
  211.   {
  212.     return (self::$_Mode && self::$_OverloadBitmask) ? (!($OvelroadBitmask & self::$_OverloadBitmask)) : self::$_Mode;
  213.   }
  214. }


Теперь, работа со строками будет проводиться в таком стиле:


Listing №5 (PHP)
  1. $RequestString = Str::Strtolower($_SERVER['REQUEST_URI']);


Если вы заметили, я описал не все функции. Для большинства задач этих будет достаточно, но если вы настаиваете, то можете дописать их сами, обратившись к документации.

Многие функции, например str_replace, не попадают в нашу категорию, поэтому вы можете оставить её как есть или, для полной унификации интерфейса работы со строками, просто добавить в разработанный класс методы с их использованием. Это сделает наш фасад более "чистым" по отношению к предмету его действия. Также вы можете не вызывать каждый раз метод _NeedUseMBFuncs, а при инициализации заполнить необходимые свойства. Оставляю это на ваше усмотрение.


Продолжение следует...

Comments
By   234234
Published   13.01.2010

епт =)

а что мешает использовать сразу mb_* ?

и не городить велосипедов

By   123123
Published   13.01.2010

да и вот ещё, почитай

http://ru2.php.net/manual/en/mbstring.configuration.php

Published   13.01.2010

злой
а что мешает использовать сразу mb_* / да и вот ещё, почитай

Слушай, почитай внимательнее и ссылками можешь не кидаться, киданый уже. И если хочешь писать mb_ - пиши, я хочу писать по-другому...

и перестань грубиянить, а?

By   24234
Published   13.01.2010

ты людей учишь, я им пишу чтобы знали что почём

Предмет цитирования
и перестань грубиянить, а?

ты же сам сказал без субъективных оценок ;)

Published   13.01.2010

я построил фасад на этой фишке, что не понятно? Написано же:

Герасимов Константин
Конечно же, многим этот подход может не понравиться, вы даже можете сказать, что это вообще не нужно делать

Почему бы это не сделать именно на этом функционале, пусть люди подумают, посмотрят что к чему, поэкспериментируют, а сказать что можно просто использовать mb_ это то же самое, что мануал тут выложить и всё...

By   234234
Published   14.01.2010

Предмет цитирования
я построил фасад на этой фишке

на фишке? :)

Предмет цитирования
Почему бы это не сделать именно на этом функционале, пусть люди подумают, посмотрят что к чему, поэкспериментируют, а сказать что можно просто использовать mb_ это то же самое, что мануал тут выложить и всё...

ну правильно, зачем пользоваться мануалом? лучше фишками а ещё лучше готовыми

Published   15.01.2010

Что ты к словам пристаешь? Я имею в виду, что никому не интересны и не нужны слова: пользуйтесь mb, а фасад он и в Африке фасад...

By   345345
Published   20.01.2010

значит делать фасад ради того чтобы был фасад?


гимн всех МВЦшнмков, делающих МВЦ чтобы был МВЦ и ООПшников, делающих ООП ради ООП

By   54345
Published   20.01.2010

книжек меньше надо читать, а больше головой думать


о боже, что я вижу...

Listing №1 (PHP)
  1.  public static function Mail($To, $Subject, $Message, $AdditionalHeaders = NULL, $AdditionalParameters = NULL)
  2.  
  3.   {
  4.  
  5.  
  6.     if (self::_NeedUseMBFuncs(1)) {
  7.  
  8.       if (NULL === $AdditionalHeaders) {
  9.  
  10.  
  11.         return mb_send_mail($To, $Subject, $Message);
  12.  
  13.       }
  14.  
  15.  
  16.       else {
  17.  
  18.         if (NULL === $AdditionalParameters) {
  19.  
  20.  
  21.           return mb_send_mail($To, $Subject, $Message, $AdditionalHeaders);
  22.  
  23.         }
  24.  
  25.  
  26.         else {
  27.  
  28.           return mb_send_mail($To, $Subject, $Message, $AdditionalHeaders, $AdditionalParameters);
  29.  
  30.  
  31.         }
  32.  
  33.       }
  34.  
  35.  
  36.     }
  37.  
  38.     else
  39.  
  40.  
  41.     {
  42.  
  43.       if (NULL === $AdditionalHeaders) {
  44.  
  45.  
  46.         return mail($To, $Subject, $Message);
  47.  
  48.       }
  49.  
  50.  
  51.       else {
  52.  
  53.         if (NULL === $AdditionalParameters) {
  54.  
  55.  
  56.           return mail($To, $Subject, $Message, $AdditionalHeaders);
  57.  
  58.         }
  59.  
  60.  
  61.         else {
  62.  
  63.           return mail($To, $Subject, $Message, $AdditionalHeaders, $AdditionalParameters);
  64.  
  65.  
  66.         }
  67.  
  68.       }
  69.  
  70.  
  71.     }
  72.  
  73.   }



Listing №2 (PHP)
  1. public static function Mail($To, $Subject, $Message, $AdditionalHeaders = NULL, $AdditionalParameters = NULL)
  2.  
  3.   {
  4.      $mail=self::_NeedUseMBFuncs(1)?"mb_send_mail":"mail";
  5.       if (!$AdditionalHeaders) {
  6.  
  7.  
  8.         return $mail($To, $Subject, $Message);
  9.  
  10.       } else {
  11.  
  12.         if (!$AdditionalParameters) {
  13.  
  14.  
  15.           return $mail($To, $Subject, $Message, $AdditionalHeaders);
  16.  
  17.         }else {
  18.  
  19.           return $mail($To, $Subject, $Message, $AdditionalHeaders, $AdditionalParameters);
  20.  
  21.         }
  22.  
  23.       }
  24.  
  25.  
  26.    
  27.  
  28.   }

Published   20.01.2010

Боже мой, какой ты молодец

By   miros
Published   24.02.2010

Код, конечно, полный кошмар. Почти во всех функция условная конструкция с одним и тем же условием, явно видно, что нужен применить две стратегии вместо них. На ужасно написанную функцию mail уже указали сверху. Да и что она делает в "Фасаде строки"? .Да и, вообще, пример на фасад очень неудачный, это - фасад только с большой натяжкой.

Published   24.02.2010

miros

Да, согласен. Я сейчас пересматриваю свои взгляды на этот цилкик статей. Проблема в том, что я леплю одно к другому, а надо решить задачу, используя шаблон, но пока это не особо выходит ))

Create comment
 
Formatting
Comment can not be edited. Please, use the button "Preview"
By
  (Enter prev char)
Comment
Categories
PHP
12
articles
Прочее
4
articles
Delphi
0
article
C/C++
0
article
C#
0
article
Java
0
article
Perl
0
article
Python
0
article
Enter
Cookie must be "ON"
Login
Password
 
Popular tags
PHP
9
articles
паттерн
5
articles
framework
5
articles
шаблон
5
articles
Template View
3
articles
Facade
3
articles
Service Stub
3
articles
Page Controller
2
articles
Singleton
2
articles
Gateway
2
articles
MySQL
2
articles
Registry
2
articles
Command
2
articles
Front Controller
2
articles
Action
2
articles
Abstract Factory
2
articles
типовое решение
2
articles
шаблоны проектирования
2
articles
Iterator
2
articles
Transaction Script
2
articles
Rambler's Top100 Правильный CSS!