PHP: Кэширование вызовов на грани фола

от автора

Сидел я как-то вечером и переписывал тонны кода в рамках расширения функционала. Код старый, кое-где костыли, но куда же без них. Бывает.А вот на «тяжелых» методах у нас кеширование, реализованное в таком вот виде:

<font size="2" face="Courier New" color="black"><br /><font color="#3A00FF"><b>class</b></font> GarlemShake <font color="#009900">{</font><br /><font color="#339933">...</font><br /><font color="#3A00FF"><b>public</b></font> <font color="#3A00FF"><b>function</b></font> getTotals<font color="#009900">(</font><font color="#000088">$client_id</font><font color="#009900">)</font> <font color="#009900">{</font><br />    <font color="#990000">static</font> <font color="#000088">$cache</font> <font color="#339933">=</font> <font color="#3A00FF"><b>null</b></font><font color="#339933">;</font><br />    <font color="#000088">$key</font> <font color="#339933">=</font> <font color="#990000">md5</font><font color="#009900">(</font><font color="#990000">serialize</font><font color="#009900">(</font><font color="#990000">func_get_args</font><font color="#009900">(</font><font color="#009900">)</font><font color="#009900">)</font><font color="#009900">)</font><font color="#339933">;</font><br />    <font color="#b1b100">if</font> <font color="#009900">(</font><font color="#990000">isset</font><font color="#009900">(</font><font color="#000088">$cache</font><font color="#009900">[</font><font color="#000088">$key</font><font color="#009900">]</font><font color="#009900">)</font><font color="#009900">)</font> <font color="#009900">{</font><br />        <font color="#666666"><i>// вычисляем долго и упорно</i></font><br />        <font color="#666666"><i>// складываем в $result</i></font><br />        <font color="#000088">$cache</font><font color="#009900">[</font><font color="#000088">$key</font><font color="#009900">]</font> <font color="#339933">=</font> <font color="#000088">$result</font><font color="#339933">;</font><br />    <font color="#009900">}</font> <font color="#b1b100">else</font> <font color="#009900">{</font><br />        <font color="#000088">$result</font> <font color="#339933">=</font> <font color="#000088">$cache</font><font color="#009900">[</font><font color="#000088">$key</font><font color="#009900">]</font><font color="#339933">;</font><br />    <font color="#009900">}</font><br />    <br />    <font color="#b1b100">return</font> <font color="#000088">$result</font><font color="#339933">;</font>    <br /><font color="#009900">}</font><br /><font color="#339933">...</font><br /><font color="#009900">}</font><br /> </font>

Повторяется почти по всему проекту.Некрасиво? Да. Долго печатать? Ну можно в сниппеты записать, и на фразу makemycache он будет выдавать заготовленный кусок кода.Ухудшает код? Читаемость уж точно.Но, как всем нам известно, лень — двигатель прогресса. Вот и мне пришло в голову упростить немного модель кеширования, максимально используя средства языка. За несколько минут накидал простенький прототип, складывающий результат в массив внутри себя.

<font size="2" face="Courier New" color="black"><br /><font color="#3A00FF"><b>class</b></font> Cacheable <font color="#009900">{</font><br />        <font color="#009933"><i>/** @var object */</i></font><br />        protected <font color="#000088">$_instance</font> <font color="#339933">=</font> <font color="#3A00FF"><b>null</b></font><font color="#339933">;</font><br />        <font color="#009933"><i>/** @var array */</i></font><br />        protected <font color="#000088">$_cache</font> <font color="#339933">=</font> <font color="#990000">array</font><font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><br />        <font color="#009933"><i>/**<br />         * Конструктор-перехватчик<br />         */</i></font> <br />        <font color="#3A00FF"><b>public</b></font> <font color="#3A00FF"><b>function</b></font> __construct<font color="#009900">(</font><font color="#009900">)</font> <font color="#009900">{</font><br />                <font color="#000088">$params</font> <font color="#339933">=</font> <font color="#990000">func_get_args</font><font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br />                <font color="#b1b100">if</font> <font color="#009900">(</font><font color="#339933">!</font><font color="#990000">empty</font><font color="#009900">(</font><font color="#000088">$params</font><font color="#009900">)</font><font color="#009900">)</font> <font color="#009900">{</font>                      <br />                        <font color="#000088">$this</font><font color="#339933">-></font>_instance <font color="#339933">=</font> <font color="#990000">is_object</font><font color="#009900">(</font><font color="#000088">$obj</font> <font color="#339933">=</font> <font color="#990000">array_shift</font><font color="#009900">(</font><font color="#000088">$params</font><font color="#009900">)</font><font color="#009900">)</font> ? <font color="#000088">$obj</font> <font color="#339933">:</font> <font color="#3A00FF"><b>new</b></font> <font color="#000088">$obj</font><font color="#009900">(</font><font color="#000088">$params</font><font color="#009900">)</font><font color="#339933">;</font><br />                <font color="#009900">}</font><br />        <font color="#009900">}</font><br />        <font color="#009933"><i>/**<br />         * Магический метод кеширования<br />         * <br />         * @param $method<br />         * @param $params<br />         *<br />         * @return mixed<br />         */</i></font><br />        <font color="#3A00FF"><b>public</b></font> <font color="#3A00FF"><b>function</b></font> __call<font color="#009900">(</font><font color="#000088">$method</font><font color="#339933">,</font> <font color="#000088">$params</font><font color="#009900">)</font> <font color="#009900">{</font><br />                <font color="#000088">$instance</font> <font color="#339933">=</font> <font color="#990000">isset</font><font color="#009900">(</font><font color="#000088">$this</font><font color="#339933">-></font>_instance<font color="#009900">)</font> ? <font color="#000088">$this</font><font color="#339933">-></font>_instance <font color="#339933">:</font> <font color="#000088">$this</font><font color="#339933">;</font><br /><br />                <font color="#000088">$key</font> <font color="#339933">=</font> <font color="#990000">crc32</font><font color="#009900">(</font><font color="#990000">serialize</font><font color="#009900">(</font><font color="#990000">array</font><font color="#009900">(</font><font color="#000088">$method</font><font color="#339933">,</font> <font color="#000088">$params</font><font color="#009900">)</font><font color="#009900">)</font><font color="#009900">)</font><font color="#339933">;</font><br />                <font color="#b1b100">if</font> <font color="#009900">(</font><font color="#339933">!</font><font color="#990000">isset</font><font color="#009900">(</font><font color="#000088">$this</font><font color="#339933">-></font><font color="#004000">cache</font><font color="#009900">[</font><font color="#000088">$key</font><font color="#009900">]</font><font color="#009900">)</font><font color="#009900">)</font> <font color="#009900">{</font><br />                        <font color="#000088">$result</font> <font color="#339933">=</font> <font color="#990000">call_user_func_array</font><font color="#009900">(</font><font color="#990000">array</font><font color="#009900">(</font><font color="#000088">$instance</font><font color="#339933">,</font> <font color="#000088">$method</font><font color="#009900">)</font><font color="#339933">,</font> <font color="#000088">$params</font><font color="#009900">)</font><font color="#339933">;</font><br />                        <font color="#000088">$this</font><font color="#339933">-></font><font color="#004000">cache</font><font color="#009900">[</font><font color="#000088">$key</font><font color="#009900">]</font> <font color="#339933">=</font> <font color="#000088">$result</font><font color="#339933">;</font><br />                <font color="#009900">}</font> <font color="#b1b100">else</font> <font color="#009900">{</font><br />                        <font color="#000088">$result</font> <font color="#339933">=</font> <font color="#000088">$this</font><font color="#339933">-></font><font color="#004000">cache</font><font color="#009900">[</font><font color="#000088">$key</font><font color="#009900">]</font><font color="#339933">;</font><br />                <font color="#009900">}</font><br />                <br />                <font color="#b1b100">return</font> <font color="#000088">$result</font><font color="#339933">;</font><br />        <font color="#009900">}</font><br /><font color="#009900">}</font><br /> </font>

Из языковой магии используются магические методы __call и __construct. Первый для перехвата вызовов (далее будет рассказан финт ушами), второй для передачи объекта в ответственные руки кэширующего механизма.Теперь пару примеров использования оборачивания вызовов

<font size="2" face="Courier New" color="black"><br /><font color="#000088">$cached_db</font> <font color="#339933">=</font> <font color="#3A00FF"><b>new</b></font> Cacheable<font color="#009900">(</font><font color="#0000ff">'db'</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#666666"><i>// ну или так</i></font><br /><font color="#000088">$db</font> <font color="#339933">=</font> <font color="#3A00FF"><b>new</b></font> DB<font color="#339933">;</font><br /><font color="#000088">$cached_db</font> <font color="#339933">=</font> <font color="#3A00FF"><b>new</b></font> Cacheable<font color="#009900">(</font><font color="#000088">$db</font><font color="#009900">)</font><font color="#339933">;</font><br /> </font>

В этом случае все вызовы методов $cached_db будут прокешированы, чуть более, чем полностью.А в чем же финт ушами? В процессе наследования. Предположим, у нас есть класс, некоторые функции которого нам хотелось бы закешировать

<font size="2" face="Courier New" color="black"><br /><font color="#3A00FF"><b>class</b></font> PartialCached <font color="#3A00FF"><b>extends</b></font> Cacheable <font color="#009900">{</font><br />    <font color="#3A00FF"><b>public</b></font> <font color="#3A00FF"><b>function</b></font> NotCached<font color="#009900">(</font><font color="#009900">)</font> <font color="#009900">{</font><br />        <font color="#666666"><i>// ...</i></font><br />    <font color="#009900">}</font><br /><br />    <font color="#3A00FF"><b>public</b></font> <font color="#3A00FF"><b>function</b></font> NotCachedAtAll<font color="#009900">(</font><font color="#009900">)</font> <font color="#009900">{</font><br />        <font color="#666666"><i>// ...</i></font><br />    <font color="#009900">}</font><br />    <br />    protected <font color="#3A00FF"><b>function</b></font> Cached<font color="#009900">(</font><font color="#009900">)</font> <font color="#009900">{</font><br />        <font color="#666666"><i>// ...</i></font><br />    <font color="#009900">}</font>   <br />    <br /><font color="#009900">}</font><br /> </font>

Посмотрев на код, вы наверняка скажете -, а где же инструкции, которые позволят указать, что кешировать, а что нет? Приглядевшись еще раз к коду, можно увидеть разницу — она в том, что при вызове protected методов наш кэширующий механизм включается, а для public кэширование отключено. И реализуется этот финт ушами средствами самого PHP, кто protected методы пропускает через __call, который, собственно, был перекрыт у родителя. Такие вот отличные подарки от движка.Как можно улучшить? Добавить парочку паттернов по вкусу (стратегии хранения кеша/хэширования ключа). Где использовать? Решайте сами, в моем случае этот оберточный класс улучшил читаемость кода на 146%. Шучу, я еще подумаю над тем, нужно ли это проекту.Набор для тестов и результаты:

<font size="2" face="Courier New" color="black"><br /><font color="#3A00FF"><b>class</b></font> a <font color="#009900">{</font><br />        <font color="#3A00FF"><b>public</b></font> <font color="#3A00FF"><b>function</b></font> longCalc<font color="#009900">(</font><font color="#000088">$a</font><font color="#009900">)</font> <font color="#009900">{</font><br />                <font color="#990000">usleep</font><font color="#009900">(</font><font color="#cc66cc">150000</font><font color="#009900">)</font><font color="#339933">;</font><br />                <font color="#b1b100">return</font> <font color="#000088">$a</font> <font color="#339933">*</font> <font color="#000088">$a</font><font color="#339933">;</font><br />        <font color="#009900">}</font><br /><font color="#009900">}</font><br /><br /><font color="#3A00FF"><b>class</b></font> c <font color="#3A00FF"><b>extends</b></font> Cacheable <font color="#009900">{</font><br />        <font color="#3A00FF"><b>private</b></font> <font color="#3A00FF"><b>function</b></font> longCalc<font color="#009900">(</font><font color="#000088">$a</font><font color="#009900">)</font> <font color="#009900">{</font><br />                <font color="#990000">usleep</font><font color="#009900">(</font><font color="#cc66cc">150000</font><font color="#009900">)</font><font color="#339933">;</font><br />                <font color="#b1b100">return</font> <font color="#000088">$a</font> <font color="#339933">*</font> <font color="#000088">$a</font><font color="#339933">;</font><br />        <font color="#009900">}</font><br /><font color="#009900">}</font><br /><br /><font color="#3A00FF"><b>function</b></font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><br /><font color="#009900">{</font><br />    <font color="#990000">list</font><font color="#009900">(</font><font color="#000088">$usec</font><font color="#339933">,</font> <font color="#000088">$sec</font><font color="#009900">)</font> <font color="#339933">=</font> <font color="#990000">explode</font><font color="#009900">(</font><font color="#0000ff">" "</font><font color="#339933">,</font> <font color="#990000">microtime</font><font color="#009900">(</font><font color="#009900">)</font><font color="#009900">)</font><font color="#339933">;</font><br />    <font color="#b1b100">return</font> <font color="#009900">(</font><font color="#009900">(</font>float<font color="#009900">)</font><font color="#000088">$usec</font> <font color="#339933">+</font> <font color="#009900">(</font>float<font color="#009900">)</font><font color="#000088">$sec</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009900">}</font><br /><br /><font color="#000088">$b</font> <font color="#339933">=</font> <font color="#3A00FF"><b>new</b></font> Cacheable<font color="#009900">(</font><font color="#0000ff">'a'</font><font color="#009900">)</font><font color="#339933">;</font><br /><br /><font color="#000088">$time_start</font> <font color="#339933">=</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009933"><i>/** @var $b a */</i></font><br /><font color="#000088">$b</font><font color="#339933">-></font><font color="#004000">longCalc</font><font color="#009900">(</font><font color="#cc66cc">1</font><font color="#009900">)</font><font color="#339933">;</font> <font color="#666666"><i>// Ожидается 0.15</i></font><br /><font color="#990000">echo</font> <font color="#990000">sprintf</font><font color="#009900">(</font><font color="#0000ff">'%.2f'</font><font color="#339933">,</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font> <font color="#339933">-</font> <font color="#000088">$time_start</font><font color="#009900">)</font> <font color="#339933">.</font> <font color="#0000ff">' sec '</font> <font color="#339933">.</font> PHP_EOL<font color="#339933">;</font><br /><br /><font color="#000088">$time_start</font> <font color="#339933">=</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009933"><i>/** @var $b a */</i></font><br /><font color="#000088">$b</font><font color="#339933">-></font><font color="#004000">longCalc</font><font color="#009900">(</font><font color="#cc66cc">2</font><font color="#009900">)</font><font color="#339933">;</font> <font color="#666666"><i>// Ожидается 0.15</i></font><br /><font color="#990000">echo</font> <font color="#990000">sprintf</font><font color="#009900">(</font><font color="#0000ff">'%.2%'</font><font color="#339933">,</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font> <font color="#339933">-</font> <font color="#000088">$time_start</font><font color="#009900">)</font> <font color="#339933">.</font> <font color="#0000ff">' sec '</font> <font color="#339933">.</font> PHP_EOL<font color="#339933">;</font><br /><br /><font color="#000088">$time_start</font> <font color="#339933">=</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009933"><i>/** @var $b a */</i></font><br /><font color="#000088">$b</font><font color="#339933">-></font><font color="#004000">longCalc</font><font color="#009900">(</font><font color="#cc66cc">1</font><font color="#009900">)</font><font color="#339933">;</font> <font color="#666666"><i>// Кеш, Ожидается 0.00</i></font><br /><font color="#990000">echo</font> <font color="#990000">sprintf</font><font color="#009900">(</font><font color="#0000ff">'%.2f'</font><font color="#339933">,</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font> <font color="#339933">-</font> <font color="#000088">$time_start</font><font color="#009900">)</font> <font color="#339933">.</font> <font color="#0000ff">' sec '</font> <font color="#339933">.</font> PHP_EOL<font color="#339933">;</font><br /><br /><font color="#000088">$c</font> <font color="#339933">=</font> <font color="#3A00FF"><b>new</b></font> c<font color="#339933">;</font><br /><br /><font color="#000088">$time_start</font> <font color="#339933">=</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009933"><i>/** @var $c c */</i></font><br /><font color="#000088">$c</font><font color="#339933">-></font><font color="#004000">longCalc</font><font color="#009900">(</font><font color="#cc66cc">1</font><font color="#009900">)</font><font color="#339933">;</font> <font color="#666666"><i>// Ожидается 0.15</i></font><br /><font color="#990000">echo</font> <font color="#990000">sprintf</font><font color="#009900">(</font><font color="#0000ff">'%.2f'</font><font color="#339933">,</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font> <font color="#339933">-</font> <font color="#000088">$time_start</font><font color="#009900">)</font> <font color="#339933">.</font> <font color="#0000ff">' sec '</font> <font color="#339933">.</font> PHP_EOL<font color="#339933">;</font><br /><br /><font color="#000088">$time_start</font> <font color="#339933">=</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009933"><i>/** @var $c c */</i></font><br /><font color="#000088">$c</font><font color="#339933">-></font><font color="#004000">longCalc</font><font color="#009900">(</font><font color="#cc66cc">2</font><font color="#009900">)</font><font color="#339933">;</font> <font color="#666666"><i>// Ожидается 0.15</i></font><br /><font color="#990000">echo</font> <font color="#990000">sprintf</font><font color="#009900">(</font><font color="#0000ff">'%.2f'</font><font color="#339933">,</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font> <font color="#339933">-</font> <font color="#000088">$time_start</font><font color="#009900">)</font> <font color="#339933">.</font> <font color="#0000ff">' sec '</font> <font color="#339933">.</font> PHP_EOL<font color="#339933">;</font><br /><br /><font color="#000088">$time_start</font> <font color="#339933">=</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font><font color="#339933">;</font><br /><font color="#009933"><i>/** @var $c c */</i></font><br /><font color="#000088">$c</font><font color="#339933">-></font><font color="#004000">longCalc</font><font color="#009900">(</font><font color="#cc66cc">1</font><font color="#009900">)</font><font color="#339933">;</font> <font color="#666666"><i>// Кеш, Ожидается 0.00</i></font><br /><font color="#990000">echo</font> <font color="#990000">sprintf</font><font color="#009900">(</font><font color="#0000ff">'%.2f'</font><font color="#339933">,</font> microtime_float<font color="#009900">(</font><font color="#009900">)</font> <font color="#339933">-</font> <font color="#000088">$time_start</font><font color="#009900">)</font> <font color="#339933">.</font> <font color="#0000ff">' sec '</font> <font color="#339933">.</font> PHP_EOL<font color="#339933">;</font><br /> </font>

______________________

Текст подготовлен в Редакторе Блогов от © SoftCoder.ru

ссылка на оригинал статьи http://habrahabr.ru/post/170779/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *