<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Php on Илья Маринин</title>
    <link>https://www.marinin.pw/ru/tags/php/</link>
    <description>Recent content in Php on Илья Маринин</description>
    <generator>Hugo -- 0.138.0</generator>
    <language>ru-RU</language>
    <lastBuildDate>Sat, 21 Mar 2020 17:19:07 +0300</lastBuildDate>
    <atom:link href="https://www.marinin.pw/ru/tags/php/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Добавление новых php-расширений в Homestead</title>
      <link>https://www.marinin.pw/ru/posts/homestead_extend/</link>
      <pubDate>Sat, 21 Mar 2020 17:19:07 +0300</pubDate>
      <guid>https://www.marinin.pw/ru/posts/homestead_extend/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://laravel.com/docs/6.x/homestead&#34;&gt;Homestead&lt;/a&gt; — это набор конфигов от сообщества &lt;a href=&#34;https://laravel.com/&#34;&gt;Laravel&lt;/a&gt; виртуальной машины на базе &lt;a href=&#34;https://www.vagrantup.com/&#34;&gt;Vagrant&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Новичкам иногда сложно сходу добавить новый функционал в виртуальную машину на базе Homestead.&lt;/p&gt;
&lt;p&gt;Для примера дан простой набор шагов по добавлению php-расширения &lt;a href=&#34;https://www.php.net/manual/ru/book.apcu.php&#34;&gt;APCu&lt;/a&gt; в Homestead. Оно используется, например, в &lt;a href=&#34;https://symfony.com/&#34;&gt;Symfony&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;настраиваем-homestead&#34;&gt;Настраиваем Homestead&lt;/h2&gt;
&lt;p&gt;Находим в папке с Homestead файл &lt;code&gt;after.sh&lt;/code&gt; и добавляем нужные строки:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#Install APCu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get -y &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    -o Dpkg::Options::&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--force-confdef&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    -o Dpkg::Options::&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--force-confold&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    install php-apcu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;пересобираем-виртуальную-машину&#34;&gt;Пересобираем виртуальную машину&lt;/h2&gt;
&lt;p&gt;В терминале в папке с Homestead выполянем:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://laravel.com/docs/6.x/homestead">Homestead</a> — это набор конфигов от сообщества <a href="https://laravel.com/">Laravel</a> виртуальной машины на базе <a href="https://www.vagrantup.com/">Vagrant</a>.</p>
<p>Новичкам иногда сложно сходу добавить новый функционал в виртуальную машину на базе Homestead.</p>
<p>Для примера дан простой набор шагов по добавлению php-расширения <a href="https://www.php.net/manual/ru/book.apcu.php">APCu</a> в Homestead. Оно используется, например, в <a href="https://symfony.com/">Symfony</a>.</p>
<h2 id="настраиваем-homestead">Настраиваем Homestead</h2>
<p>Находим в папке с Homestead файл <code>after.sh</code> и добавляем нужные строки:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#Install APCu</span>
</span></span><span style="display:flex;"><span>sudo apt-get -y <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -o Dpkg::Options::<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;--force-confdef&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    -o Dpkg::Options::<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;--force-confold&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    install php-apcu
</span></span></code></pre></div><h2 id="пересобираем-виртуальную-машину">Пересобираем виртуальную машину</h2>
<p>В терминале в папке с Homestead выполянем:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ vagrant destroy
</span></span><span style="display:flex;"><span>$ vagrant up --provision
</span></span></code></pre></div><h2 id="проверяем-результат">Проверяем результат</h2>
<p>Там же выполняем:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ vagrant ssh
</span></span></code></pre></div><p>Внутри виртуальной машины выполняем:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ php -m | grep apcu
</span></span></code></pre></div><p>Мы должны увидеть в выводе <code>apcu</code>.</p>
<p>Аналогичным образом можно установить остальные недостающие расширения.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Отладка кода в docker-контейнере из NetBeans</title>
      <link>https://www.marinin.pw/ru/posts/netbeans-docker-xdebug/</link>
      <pubDate>Sat, 27 Jan 2018 20:36:21 +0500</pubDate>
      <guid>https://www.marinin.pw/ru/posts/netbeans-docker-xdebug/</guid>
      <description>&lt;p&gt;Я уже немного писал о NetBeans &lt;a href=&#34;https://www.marinin.pw/post/netbeans_hotkeys&#34;&gt;тут&lt;/a&gt; . Данная IDE умеет хорошо дружить с Xdebug, в связке с которым очень удобно отлаживать php-код. Основы успешно описаны, например, &lt;a href=&#34;https://netbeans.org/kb/docs/php/debugging_ru.html&#34;&gt;здесь&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Все интереснее, когда код приходится отлаживать не на локальной машине, а внутри docker-контейнера. Особенно, если нужно отладить cli-скрипт внутри контейнера.&lt;/p&gt;
&lt;p&gt;Для успешной отладки следует:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Настроить docker-контейнер с Xdebug внутри;&lt;/li&gt;
&lt;li&gt;Настроить Xdebug внутри контейнера;&lt;/li&gt;
&lt;li&gt;Настроить NetBeans.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;При обычной отладке с использованием браузера и при отладке cli-скрипта настройки будут слегка отличаться. Итак, поехали.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Я уже немного писал о NetBeans <a href="/post/netbeans_hotkeys">тут</a> . Данная IDE умеет хорошо дружить с Xdebug, в связке с которым очень удобно отлаживать php-код. Основы успешно описаны, например, <a href="https://netbeans.org/kb/docs/php/debugging_ru.html">здесь</a>.</p>
<p>Все интереснее, когда код приходится отлаживать не на локальной машине, а внутри docker-контейнера. Особенно, если нужно отладить cli-скрипт внутри контейнера.</p>
<p>Для успешной отладки следует:</p>
<ol>
<li>Настроить docker-контейнер с Xdebug внутри;</li>
<li>Настроить Xdebug внутри контейнера;</li>
<li>Настроить NetBeans.</li>
</ol>
<p>При обычной отладке с использованием браузера и при отладке cli-скрипта настройки будут слегка отличаться. Итак, поехали.</p>
<h2 id="настройка-контейнера">Настройка контейнера</h2>
<p>Установку Xdebug внутрь контейнера мы обсуждать не будем. Эти настройки можно найти, например, <a href="https://github.com/apparena/docker-php7-xdebug/blob/master/Dockerfile">тут</a>.</p>
<p>Интереснее настройки самого Xdebug.</p>
<p>В случае с обычной отладкой:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#a6e22e">xdebug.remote_enable</span><span style="color:#f92672">=</span><span style="color:#e6db74">1</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">xdebug.remote_connect_back</span><span style="color:#f92672">=</span><span style="color:#e6db74">1</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">xdebug.idekey</span><span style="color:#f92672">=</span><span style="color:#e6db74">netbeans-xdebug</span>
</span></span></code></pre></div><p>Расшифровка:</p>
<ul>
<li><code>xdebug.remote_enable</code> — разрешает удаленную отладку.</li>
<li><code>xdebug.remote_connect_back</code> — автоматическое установление соединения с хостом, который иницирует удаленную отладку. Если не указывать эту опцию, то нужно использовать <code>xdebug.remote_host</code>, где следует указать ip вашего хоста из виртуального docker-адаптера.</li>
<li><code>xdebug.idekey</code> — ключ для соединения. Значение <code>netbeans-xdebug</code> задано в NetBeans по умолчанию. Можно не задавать эту опцию и использовать <code>xdebug.remote_autostart=1</code>, но тогда Xdebug будет стартовать в каждом скрипте, даже если это не нужно, что скажется на производительности.</li>
</ul>
<h2 id="настройка-netbeans">Настройка NetBeans</h2>
<p>Для успешной отладки NetBeans должен знать как сопоставить между собой пути внутри docker-контейнера и пути в хостовой машине.</p>
<p>Идем в <strong>Свойства проекта</strong>, далее <strong>Выполнить настройку → Дополнительно</strong>. Внизу в разделе <strong>Отображение пути</strong> добавляем наше сопоставление.</p>
<p>Теперь, если запустить отладку проекта, то отладчик должен автоматически остановить выполнение на первой строке скрипта точки входа в приложение (если активирована соответствующая настройка в IDE).</p>
<p>Если ничего не произошло и вас отключена опция NetBeans <strong>Остановить в первой строчке</strong>, то удостоверьтесь, что поставили breakpoint где-то по пути выполнения скрипта. Если ни одного breakpoint не установлено, то следует установить его, остановить отладку и запустить снова.</p>
<h2 id="отладка-cli-скрипта">Отладка cli-скрипта</h2>
<p>Для отладки cli-скрипта все немного иначе. Главное отличие в том, что опция <code>xdebug.remote_connect_back=1</code> нам не нужна, ведь соединения от хостовой машины не будет. Вместо этого NetBeans будет ожидать обращений от приложения.</p>
<p>Для начала настроим NetBeans.</p>
<p>Идем по уже известному пути <strong>Свойства проекта → Выполнить настройку</strong>. Здесь добавляем новую конфигурацию, нажав <strong>Создать&hellip;</strong>. Далее жмем <strong>Дополнительно</strong> и указываем опцию <strong>Не открывать браузер</strong>.</p>
<p>После настройки, кликаем правой кнопкой по проекту, выбираем <strong>Установить конфигурацию</strong> и задаем вариант, который только что создали.</p>
<p>Теперь можно нажать <strong>Отладка проекта</strong> или просто <strong>Ctrl+F5</strong>. Браузер не должен открыться.</p>
<p>Идем в меню <strong>Окно → IDE и сервис → Терминал</strong>. В появившемся терминале выполняем вход в docker-контейнер, выполнив что-то вроде:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ docker exec -ti %container_id% bash
</span></span></code></pre></div><p><strong>%container_id%</strong> в данном случае id контейнера с вашим приложением.</p>
<p>Теперь самое интересное. Выполняем:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ export XDEBUG_CONFIG<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;remote_enable=1 remote_mode=req remote_port=9000 remote_host=%remote_host_ip% remote_connect_back=0 idekey=netbeans-xdebug&#34;</span>
</span></span></code></pre></div><p>Обратите внимание на указанный <strong>remote_host</strong>. Именно по этому адресу Xdebug будет ломиться в отладчик. Проставьте свое значение на место <strong>%remote_host_ip%</strong>.</p>
<p>Теперь можно запустить какой-нибудь cli-скрипт. Например, миграции yii:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./yii migrate
</span></span></code></pre></div><p>Если отладчик NetBeans запущен, то он должен отловить это событие и прервать выполнение на первой строке скрипта точки входа.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Реализация RESTful API в Yii2</title>
      <link>https://www.marinin.pw/ru/posts/yii2-rest/</link>
      <pubDate>Sun, 07 Jan 2018 15:40:09 +0500</pubDate>
      <guid>https://www.marinin.pw/ru/posts/yii2-rest/</guid>
      <description>&lt;p&gt;Yii2 из коробки предоставляет удобный фреймворк для построения REST API вашего сервиса. Ниже я опишу как быстро начать его использовать.&lt;/p&gt;
&lt;h2 id=&#34;предварительная-настройка&#34;&gt;Предварительная настройка&lt;/h2&gt;
&lt;p&gt;Для начала работы достаточно &lt;a href=&#34;https://github.com/yiisoft/yii2-app-basic&#34;&gt;базового шаблона&lt;/a&gt; Yii2. Создание проекта и настройку виртуального хоста я описывать не буду и перейду сразу к настройке REST.&lt;/p&gt;
&lt;p&gt;После скачивания проекта настроим компоненту &lt;code&gt;user&lt;/code&gt; в файле &lt;code&gt;web.php&lt;/code&gt;. Добавим туда следующие строки:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;enableSession&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;loginUrl&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Первая строка отключает сохранение состояния пользователя на сервере: одно из требований архитектуры REST.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Yii2 из коробки предоставляет удобный фреймворк для построения REST API вашего сервиса. Ниже я опишу как быстро начать его использовать.</p>
<h2 id="предварительная-настройка">Предварительная настройка</h2>
<p>Для начала работы достаточно <a href="https://github.com/yiisoft/yii2-app-basic">базового шаблона</a> Yii2. Создание проекта и настройку виртуального хоста я описывать не буду и перейду сразу к настройке REST.</p>
<p>После скачивания проекта настроим компоненту <code>user</code> в файле <code>web.php</code>. Добавим туда следующие строки:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#e6db74">&#39;enableSession&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">false</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;loginUrl&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">null</span>,
</span></span></code></pre></div><p>Первая строка отключает сохранение состояния пользователя на сервере: одно из требований архитектуры REST.</p>
<p>Вторая строка отключает перенаправление на страницу входа после неудачной аутентификации.</p>
<p>В <code>User.php</code> добавим следующий код:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getAll</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>   $result <span style="color:#f92672">=</span> [];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">foreach</span> (<span style="color:#a6e22e">self</span><span style="color:#f92672">::</span>$users <span style="color:#66d9ef">as</span> $user) {
</span></span><span style="display:flex;"><span>       $result[] <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>           <span style="color:#e6db74">&#39;id&#39;</span> <span style="color:#f92672">=&gt;</span> $user[<span style="color:#e6db74">&#39;id&#39;</span>],
</span></span><span style="display:flex;"><span>           <span style="color:#e6db74">&#39;username&#39;</span> <span style="color:#f92672">=&gt;</span> $user[<span style="color:#e6db74">&#39;username&#39;</span>],
</span></span><span style="display:flex;"><span>       ];
</span></span><span style="display:flex;"><span>   }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">return</span> $result;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="создание-контроллера">Создание контроллера</h2>
<p>Мы не используем базу данных в нашем примере и создание контроллера займет чуть больше времени, чем в случае использования AR-моделей.</p>
<p>В папке <code>controllers</code> создадим файл <code>UserController.php</code> со следующим содержимым:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#f92672">&lt;?</span><span style="color:#a6e22e">php</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> <span style="color:#a6e22e">app\controllers</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">yii\rest\Controller</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">app\models\User</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UserController</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">Controller</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">actionIndex</span>()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">User</span><span style="color:#f92672">::</span><span style="color:#a6e22e">getAll</span>();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">/**
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">     * @inheritdoc
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">     */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">protected</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">verbs</span>()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;index&#39;</span> <span style="color:#f92672">=&gt;</span> [<span style="color:#e6db74">&#39;GET&#39;</span>, <span style="color:#e6db74">&#39;HEAD&#39;</span>],
</span></span><span style="display:flex;"><span>        ];
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Как видно из примера, мы наследуем свой класс от <code>yii\rest\Controller</code>. Данный класс реализует поддержку сериализации и представления массивов в форматах XML и JSON перед отправкой ответа. Дополнительно можно задать параметры аутентификации и ограничить количесто запросов к API.</p>
<p>Метод <code>verbs</code> позволяет задать типы запросов, которые принимает каждое из действий контроллера.</p>
<p>Чтобы контроллер заработал, нужно отредактировать настройки компоненты <code>urlManager</code> в файле <code>web.php</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#e6db74">&#39;rules&#39;</span> <span style="color:#f92672">=&gt;</span> [
</span></span><span style="display:flex;"><span>    [<span style="color:#e6db74">&#39;class&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;yii\rest\UrlRule&#39;</span>, <span style="color:#e6db74">&#39;controller&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;user&#39;</span>],
</span></span><span style="display:flex;"><span>],
</span></span></code></pre></div><p>Протестируем работу API, используя curl:</p>
<pre tabindex="0"><code>$ curl -i -H &#34;Accept:application/json&#34; &#34;http://yii2-rest.dev/users&#34;
$ curl -i -H &#34;Accept:application/xml&#34; &#34;http://yii2-rest.dev/users&#34;
</code></pre><blockquote>
<p>Вместо yii2-rest.dev нужно указать имя вашего хоста.</p>
</blockquote>
<h2 id="аутентификация">Аутентификация</h2>
<blockquote>
<p>При реализации любого из методов аутентификации в продакшене не забывайте об использовании https для предотвращения атаки типа &ldquo;человек посередине&rdquo;</p>
</blockquote>
<p>Yii2 поддерживает <a href="http://www.yiiframework.com/doc-2.0/guide-rest-authentication.html">несколько</a> типов аутентификации. Реализуем <strong>HTTP Basic Auth</strong>.</p>
<p>В <code>UserContoller.php</code> добавим следующие строки:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">behaviors</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    $behaviors <span style="color:#f92672">=</span> <span style="color:#66d9ef">parent</span><span style="color:#f92672">::</span><span style="color:#a6e22e">behaviors</span>();
</span></span><span style="display:flex;"><span>    $behaviors[<span style="color:#e6db74">&#39;authenticator&#39;</span>] <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;class&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">HttpBasicAuth</span><span style="color:#f92672">::</span><span style="color:#a6e22e">className</span>(),
</span></span><span style="display:flex;"><span>    ];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> $behaviors;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Не забываем перед объявлением класса прописать:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">use</span> <span style="color:#a6e22e">yii\filters\auth\HttpBasicAuth</span>;
</span></span></code></pre></div><p>Чтобы все заработало, модель пользователя должна реализовывать метод <code>findIdentityByAccessToken</code>. Если вы используете базовый шаблон Yii2, то он уже реализован и ничего менять не нужно.</p>
<p>Проверям работу механизма аутентификации:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">$</span> <span style="color:#a6e22e">curl</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">i</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">H</span> <span style="color:#e6db74">&#34;Accept:application/json&#34;</span> <span style="color:#e6db74">&#34;http://yii2-rest.dev/users&#34;</span>
</span></span></code></pre></div><p>Выполнение кода выше должно вернуть ошибку с кодом 401.</p>
<p>Пробуем авторизоваться, используя HTTP Basic Auth:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">$</span> <span style="color:#a6e22e">curl</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">i</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">H</span> <span style="color:#e6db74">&#34;Accept:application/json&#34;</span> <span style="color:#e6db74">&#34;http://yii2-task_manager.dev/users&#34;</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">H</span> <span style="color:#e6db74">&#34;Authorization: Basic MTAwLXRva2VuOg==&#34;</span>
</span></span></code></pre></div><p>Должен вернуться полный список пользователей в json-ответе.</p>
<h2 id="заключение">Заключение</h2>
<p>Я описал лишь один из вариантов реализации REST в Yii2. Без рассмотрения остались множество других тем, таких как версионирование, ограничение числа запросов и обработка ошибок. Многое из этого описано в <a href="http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html">документации</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Основы PHP_CodeSniffer</title>
      <link>https://www.marinin.pw/ru/posts/php_code_sniffer/</link>
      <pubDate>Sat, 18 Nov 2017 20:59:11 +0500</pubDate>
      <guid>https://www.marinin.pw/ru/posts/php_code_sniffer/</guid>
      <description>&lt;p&gt;В некоторых компаниях есть большая база долгоиграющих и постоянно изменяющихся проектов. Без неусыпного контроля база кода рискует превратиться в месиво из малочитаемых файлов. Вручную проверять код на соответствие стандартам занятие достаточно утомительное. Быстро появляется потребность в атоматизации процесса.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/squizlabs/PHP_CodeSniffer&#34;&gt;PHP_CodeSniffer&lt;/a&gt; - это инструмент для проверки кода на соответствие принятым соглашениям и (в некоторых случаях) автоматического его исправления после проверки.&lt;/p&gt;
&lt;h2 id=&#34;установка-и-запуск&#34;&gt;Установка и запуск&lt;/h2&gt;
&lt;p&gt;В самом простом случае можно выполнить такой код в папке с исходниками проекта:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>В некоторых компаниях есть большая база долгоиграющих и постоянно изменяющихся проектов. Без неусыпного контроля база кода рискует превратиться в месиво из малочитаемых файлов. Вручную проверять код на соответствие стандартам занятие достаточно утомительное. Быстро появляется потребность в атоматизации процесса.</p>
<p><a href="https://github.com/squizlabs/PHP_CodeSniffer">PHP_CodeSniffer</a> - это инструмент для проверки кода на соответствие принятым соглашениям и (в некоторых случаях) автоматического его исправления после проверки.</p>
<h2 id="установка-и-запуск">Установка и запуск</h2>
<p>В самом простом случае можно выполнить такой код в папке с исходниками проекта:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
</span></span><span style="display:flex;"><span>$ php phpcs.phar -h
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$ curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar
</span></span><span style="display:flex;"><span>$ php phpcbf.phar -h
</span></span></code></pre></div><p>Так же поддерживается установка через composer. Далее будет рассматриваться именно этот вариант.</p>
<p>Пропишем в composer.json следующий код:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;require-dev&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;squizlabs/php_codesniffer&#34;</span>: <span style="color:#e6db74">&#34;3.*&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>После обновления composer пробуем выполнить</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcs -h
</span></span><span style="display:flex;"><span>$ ./vendor/bin/phpcbf -h
</span></span></code></pre></div><p>Первый скрипт проверяет код на соответствие стандартам и выдает отчет. Второй - исправляет, то что можно исправить автоматически.</p>
<p>Чтобы посмотреть перечень доступных стандартов, можно выполнить</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcs -i
</span></span></code></pre></div><p>Для вывода списка проверок интересующего стандарта (в данном случае PSR-2) выполняем:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcs --standard<span style="color:#f92672">=</span>PSR2 -e
</span></span></code></pre></div><h2 id="проверка-кода">Проверка кода</h2>
<p>К примеру, при кодировании мы хотим следовать стандарту <a href="http://www.php-fig.org/psr/psr-2/">PSR-2</a>.</p>
<p>Выполняем в консоли:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcs --standard<span style="color:#f92672">=</span>PSR2 -p ./
</span></span></code></pre></div><p>Данная команда проверит все файлы проекта на соответствие PSR-2. Все файлы нам не нужны, поэтому некоторые мы можем смело исключить:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcs --standard<span style="color:#f92672">=</span>PSR2 -p --ignore<span style="color:#f92672">=</span>*/vendor/*,*/tests/*,*.css,*.js ./
</span></span></code></pre></div><p>В примере выше я исключил папки с тестами проекта, а так же третьесторонние модули и файлы js-скриптов и стилей css (PHP_CodeSniffer знает некоторые проверки для css и js-файлов тоже).</p>
<p>Если до первого запуска скрипта код никаким стандартам не соответствовал, то в логе нас ждет большая куча сообщений.</p>
<p>Быстро исправить некоторые простые вещи поможет выполнение следующего кода:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcbf --standard<span style="color:#f92672">=</span>PSR2 -p --ignore<span style="color:#f92672">=</span>*/vendor/*,*/tests/*,*.css,*.js ./
</span></span></code></pre></div><p>Затем можно снова вызвать <code>phpcs</code> и доправить остальные замечания вручную.</p>
<h2 id="что-дальше">Что дальше?</h2>
<p>В идеале PHP_CodeSniffer должен запускаться с определенной периодичностью. Желательно перед сдачей кода в master-ветку проекта. Оставим этот вопрос за пределами данной заметки.</p>
<p>Если в вашей компании есть свой ни на что не похожий code-style, то здесь вам поможет <a href="https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml">написание собственного набора правил</a> в формате xml.</p>
<p>В данном файле можно описать какие проверки следует выполнять, с какими параметрами запускать скрипты PHP_CodeSniffer и прочее.</p>
<p>После написания собственного набора правил, подключить его можно просто:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./vendor/bin/phpcs --standard<span style="color:#f92672">=</span>path_to_my_ruleset/ruleset.xml
</span></span></code></pre></div><p>Желаю успеха в борьбе с беспорядком в коде!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Неочевидные моменты при работе с assets в Yii2</title>
      <link>https://www.marinin.pw/ru/posts/yii2-assets/</link>
      <pubDate>Fri, 24 Feb 2017 15:29:39 +0500</pubDate>
      <guid>https://www.marinin.pw/ru/posts/yii2-assets/</guid>
      <description>&lt;p&gt;Недавно пришлось настраивать сжатие ассетов Yii2 на одном из проектов. Для этого во фреймворке предусмотрен удобный cli-интерфейс. Работа с ним вкратце описана в &lt;a href=&#34;http://www.yiiframework.com/doc-2.0/guide-structure-assets.html#combining-compressing-assets&#34;&gt;документации&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ниже перечислен ряд моментов, которые для меня оказались не очевидны.&lt;/p&gt;
&lt;h2 id=&#34;advanced-шаблон-и-алиас-app&#34;&gt;Advanced-шаблон и алиас @app&lt;/h2&gt;
&lt;p&gt;В проекте я использую &lt;a href=&#34;https://github.com/yiisoft/yii2-app-advanced&#34;&gt;advanced-шаблон&lt;/a&gt;. Он позволяет разделять проект на три части:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Для пользователя (frontend)&lt;/li&gt;
&lt;li&gt;Для администраторов и контент-менеджеров (backend)&lt;/li&gt;
&lt;li&gt;Для работы с cli-интерфейсом приложения (console)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Для каждой из частей проекта так же предусмотрены алиасы &lt;code&gt;@frontend&lt;/code&gt;, &lt;code&gt;@backend&lt;/code&gt; и &lt;code&gt;@console&lt;/code&gt; соответственно.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Недавно пришлось настраивать сжатие ассетов Yii2 на одном из проектов. Для этого во фреймворке предусмотрен удобный cli-интерфейс. Работа с ним вкратце описана в <a href="http://www.yiiframework.com/doc-2.0/guide-structure-assets.html#combining-compressing-assets">документации</a>.</p>
<p>Ниже перечислен ряд моментов, которые для меня оказались не очевидны.</p>
<h2 id="advanced-шаблон-и-алиас-app">Advanced-шаблон и алиас @app</h2>
<p>В проекте я использую <a href="https://github.com/yiisoft/yii2-app-advanced">advanced-шаблон</a>. Он позволяет разделять проект на три части:</p>
<ul>
<li>Для пользователя (frontend)</li>
<li>Для администраторов и контент-менеджеров (backend)</li>
<li>Для работы с cli-интерфейсом приложения (console)</li>
</ul>
<p>Для каждой из частей проекта так же предусмотрены алиасы <code>@frontend</code>, <code>@backend</code> и <code>@console</code> соответственно.</p>
<p>Однако у моих ассетов во frontend-части в свойстве <code>sourcePath</code> был прописан алиас <code>@app</code> вместо <code>@frontend</code>.</p>
<p>В этом не было ничего страшного до тех пор, пока не стало нужно выполнить минификацию ресурсов с помощью предоставляемого фреймворком инструментария.</p>
<p>Как выяснилось, алиас <code>@app</code> для консоли в случае с advanced-шаблоном указывает на алиас <code>@console</code> и ассеты пытались распаковаться туда, что и сбивало с толку инструмент. Тот случай, когда неплохо бы лишний раз читать <a href="https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/structure-path-aliases.md">доки</a>. :)</p>
<p><strong>Вывод:</strong> в случае использования advanced-шаблона, нужно проверить, что ассеты, расположенные во frontend/assets используют в <code>sourcePath</code> алиас <code>@frontend</code> вместо <code>@app</code>. Аналогично и для backend-части приложения.</p>
<h2 id="ошибка-file_put_contents">Ошибка file_put_contents</h2>
<p>Функция file_put_contents падает с ошибкой в случае несуществующего пути до папки.</p>
<p><strong>Вывод:</strong> следует проверять, что все пути к папкам в конфиге сжатия ассетов верные.</p>
<h2 id="невозможность-доступа-к-baseurl-ассета-после-минификации">Невозможность доступа к baseUrl ассета после минификации</h2>
<p>На заре создания проекта я хранил всю frontend-статику в папке assets. Путь до папки с финальными ассетами я получал комбинацией:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$asset <span style="color:#f92672">=</span> <span style="color:#a6e22e">frontend\assets\AppAsset</span><span style="color:#f92672">::</span><span style="color:#a6e22e">register</span>($this);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> $asset<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">baseUrl</span>;
</span></span></code></pre></div><p>Оказалось, это плохая практика. После сжатия свойство <code>baseUrl</code> у ассетов зануляется и картинки, шрифты и прочие вещи, которые до этого прекрасно отображались, перестают работать в production-окружении (в dev-окружении все в порядке).</p>
<p>Пришлось вынести папки со шрифтами и картинками в публичную часть сайта, а относительные пути в css переделать на абсолютные.</p>
<p><strong>Вывод:</strong> в assets лучше хранить только то, что будем сжимать. Остальное выносить в публичную часть. К тому же так лишний раз не копируются большие объемы данных.</p>
<h2 id="не-все-расширения-фреймворка-совместимы-с-инструментом-сжатия-ассетов">Не все расширения фреймворка совместимы с инструментом сжатия ассетов</h2>
<p>Об этом прямо указывается в <a href="http://www.yiiframework.com/doc-2.0/guide-structure-assets.html#combining-compressing-assets">документации</a> в желтой сноске в конце параграфа <em>An Example</em>, но я помучался с этой ошибкой.</p>
<p>Выглядит это так: ассеты прекрасно сжимаются, но на странице подключаются дважды (минифицированная версия и еще раз несовместимые ассеты из отдельной папки).</p>
<p>В итоге эти расширения пришлось исключить из минификации, а подключаются они на странице отдельно как и раньше, что, конечно же, увеличивает число запросов к серверу.</p>
<p><strong>Вывод:</strong> иметь в виду эту особенность при работе с расширениями использующими динамическое задание параметров ассетов в методе <code>init()</code>.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
