<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>evilbloodydemon</title>
    <link href="http://evilbloodydemon.ru/feed/" rel="self"/>
    <link href="http://evilbloodydemon.ru"/>
    <updated>2019-08-15T07:17:13.865Z</updated>
    <id>http://evilbloodydemon.ru/</id>
    <author>
        <name>evilbloodydemon</name>
        <email>evilbloodydemon@gmail.com</email>
    </author>

    
    <entry>
        <title>SEO для сайта на AngularJS своими руками</title>
        <link href="http://evilbloodydemon.ru/blog/2014/11/diy-angularjs-seo-with-phantomjs/"/>
        <updated>2014-11-15T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2014-11-15,/blog/2014/11/diy-angularjs-seo-with-phantomjs/</id>
        <content type="html"><![CDATA[<p>Одностраничные приложения на AngularJS всем хорошои, кроме того, что поисковые системы пока что не могут (или не хотят) нормально индексировать
    их страницы. Часть ботов не умеет яваскрипт полностью, другие - в очень ограниченном объёме.</p>
<p>В наличии имеется <a href="http://evilbloodydemon.ru/projects/torronto/">AJAX сайт</a> за nginx на CentOS 6, хочется обойтись минимальными телодвижениями для
    обеспечения его индексации. Основная идея - исполнять приложение в каком-нибудь headless браузере и отдавать результат поисковику.</p>
<h2 id="-">Подготовка к сканированию</h2>
<p>В секцию head главной страницы вставляем <strong><code>&lt;meta name=”fragment” content=”!” /&gt;</code></strong>. Увидев такое, поисковики понимают,
    что на этой странице есть содержимое, которое генерируется яваскриптом, но при этом доступное по запросу, сформированному
    специальным образом. Подробности и полная спецификация есть у <a href="https://developers.google.com/webmasters/ajax-crawling/docs/getting-started">гугла</a> и <a href="https://help.yandex.ru/webmaster/robot-workings/ajax-indexing.xml">яндекса</a>. </p>
<p>Вкратце, происходит вот что:</p>
<!-- cut -->

<ul>
<li>Поисковик заходит на главную страницу сайта и видит мета-тэг &#39;fragment&#39;</li>
<li>Теперь все сссылки на этой странице, которые начинаются c <code>#!</code> будут заменениы на <code>?_escaped_fragment=</code>. То есть,
  было <code>#!/movies/1</code> становится <code>?_escaped_fragment=/movies/1</code>. по спецификации еще положено, что значение параметра будет 
  подвергнуто urlencode, но на практике роботы не всегда так делают</li>
<li>Поисковик запрашивает у сервера данные по ссылкам в новом формате и сервер должен отдавать такой же html, какой видит
  пользователь в браузере</li>
</ul>
<h2 id="-html">Рендеринг HTML</h2>
<p>Для получения хтмл будем использовать <a href="http://phantomjs.org/">PhantomJS</a>. Проще всего его установить через <a href="https://www.npmjs.org/">npm</a>, у меня он уже был установлен,
    если у вас нет, то <strong><code>yum install npm</code></strong> или что-то этом роде, а потом</p>
<pre><code class="lang-bash">npm install phantomjs</code></pre>
<p>PhantomJS управляется яваскриптом и к тому же имеет встроенный веб-сервер, чем мы и воспользуемся. Вот простейший скрипт
    для отрисовки страниц:</p>
<pre><code class="lang-js">var server = require(&#39;webserver&#39;).create();
var port = 19003;

var getPage = function(url, callback) {
    var page = require(&#39;webpage&#39;).create();

    // запрашиваем страницу, ждём 200 мс, чтобы ангуляр успел завершить работу
    // можно попросить сайт отмечать готовность самостоятельно, но это потребует изменения приложения
    // а у нас все-таки простое решение
    page.open(url, function() {
        setTimeout(function() {
            page.evaluate(function() {
                // удаляем мета-тэг фрагмента, иначе контент не будет проиндексирован
                // заодно удалим скрипты, потому что отдаём готовый html
                $(&#39;meta[name=fragment], script&#39;).remove()
            });

            callback(page.content);
            page.close();
        }, 200);
    });
};

// запускаем встроенный в phantomjs веб-сервер
server.listen(port, function(request, response) {
    response.headers = {
        &#39;Content-Type&#39;: &#39;text/html&#39;
    };

    var regexp = /_escaped_fragment_=(.*)$/;
    var fragment = request.url.match(regexp);

    var url = &#39;http://localhost:19002/#!&#39; + decodeURIComponent(fragment[1]);

    // получаем хтмл и отдаём роботу
    getPage(url, function(content) {
        response.statusCode = 200;
        response.write(content);
        response.close();
    })
});</code></pre>
<p>Запускаем скрипт</p>
<pre><code>phantomjs renderbot.js</code></pre>
<p>Проверяем результат работы</p>
<pre><code>wget http://localhost:19003/?_escaped_fragment_=/movies/1</code></pre>
<p>Можно и из браузера. Должен вернуться html полноценной страницы.</p>
<h2 id="-">Отправлем решение на живой сервер</h2>
<p>В nginx внесем небольшие изменения</p>
<pre><code>if ($args ~* _escaped_fragment_) {
    proxy_pass http://localhost:19003; #phantomjs
}

proxy_pass http://127.0.0.1:19002; #оригинальный сайт</code></pre>
<p>В общем, алогритм такой:</p>
<ul>
<li>Nginx обрабатывает все запросы как обычно и передаёт их бэкэнду на порт 19002</li>
<li>До тех пор, пока не встречает в урл <code>_escaped_fragment_</code>. очевидно, что это запрос от робота поисковика (ну или от 
  кого-то очень любопытного), такой запрос nginx передаёт серверу phantomjs на порт 19003</li>
<li>Наш скрипт для phantomjs вычленяет все содержимое урл от <code>_escaped_fragment_</code> до конца строки, раскодирует его и 
  делает запрос уже с хэштэгом к бэкэнду, даёт неторое время на выполенения яваскрипта на странице и возвращает
  полученный хтмл обратно поисковому роботу</li>
</ul>
<h2 id="-">Что дальше?</h2>
<p>В посте представлено решение в общих чертах, его уже можно применять в продакшене. Но вожможно, потребуется добавить кэширование
    отрисованных страниц и обработку ошибок (таймауты и тому подобное) в скрипте phantomjs. Можно сделать это самому, а можно воспользоваться
    различными сервисами, которые все сделают сами за некоторое вознаграждение, например, <a href="http://prerender.io/">prerender.io</a> сотоварищи. 
    It&#39;s up to you.</p>
]]></content>
    </entry>
    
    <entry>
        <title>Программное добавление HTTPS-привязки к сайту IIS</title>
        <link href="http://evilbloodydemon.ru/blog/2014/11/iis-https-binding-dotnet/"/>
        <updated>2014-11-05T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2014-11-05,/blog/2014/11/iis-https-binding-dotnet/</id>
        <content type="html"><![CDATA[<p>В рабочем проекте на ASP.NET MVC понадобилось дать пользователям сайта возможность самим добавлять доменные имена для 
существующего сайта.</p>
<p>Для управления IIS 7+ существует библиотека <a href="https://www.nuget.org/packages/Microsoft.Web.Administration/">Microsoft.Web.Administration</a>.
Добавляем её в проект через NuGet</p>
<pre><code class="lang-bash">Install-Package Microsoft.Web.Administration</code></pre>
<!-- cut -->

<p>HTTP-привязка </p>
<ul>
<li>создаём инстанс ServerManager </li>
<li>находим сайт</li>
<li>добавляем домен</li>
<li>не забываем сохранить изменения.</li>
</ul>
<p>Формат записи домена - IP:Port:Hostname</p>
<p>Упрощенно выглядит так</p>
<pre><code class="lang-csharp">using (var iisManager = new ServerManager())
{
    var site = iisManager.Sites[&quot;SiteName&quot;];

    site.Bindings.Add(string.Format(&quot;*:{0}:{1}&quot;, 80, &quot;user1.localtest.me&quot;), &quot;http&quot;);

    iisManager.CommitChanges();
}</code></pre>
<p>C HTTPS слегка сложнее, потому что нужен сертификат. Теоретически, можно генерировать CSR, отдавать его пользователю и 
получать от него CRT. Но на мой взгляд это слишком эпично, поэтому от пользователя будем требовать PKCS12 (PFX)</p>
<ul>
<li>открываем сертификат</li>
<li>добавляем в хранилище</li>
<li>добавляем домен, так же как и для HTTP, но еще передаём хэш сертификата и имя хранилища</li>
<li>говорим IIS, чтобы он требовал указания имени хоста для HTTPS запросов, иначе невозможно использовать несколько сертификатов
  для одного IP</li>
</ul>
<pre><code class="lang-csharp">using (var iisManager = new ServerManager())
{
    //...
    var certificate = new X509Certificate2(&quot;path/to/certificate.pfx&quot;, &quot;certificate_password&quot;, 
        X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);

    var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
    store.Add(certificate);
    store.Close();

    var hash = certificate.GetCertHash();
    var storeName = store.Name;
    var bindingData = string.Format(&quot;*:{0}:{1}&quot;, 443, &quot;user1.localtest.me&quot;);

    var binding = site.Bindings.Add(bindingData, hash, storeName);
    binding.Protocol = &quot;https&quot;;
    binding.SetAttributeValue(&quot;sslFlags&quot;, 1);
    //...
}</code></pre>
<p>Для успешной работы нужно, чтобы у текущего процесса были административные права.</p>
]]></content>
    </entry>
    
    <entry>
        <title>CakePHP — MongoDB session storage</title>
        <link href="http://evilbloodydemon.ru/blog/2011/11/cakephp-mongodb-session-storage/"/>
        <updated>2011-04-01T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2011-04-01,/blog/2011/11/cakephp-mongodb-session-storage/</id>
        <content type="html"><![CDATA[<p>Для того, чтобы сохранять сессии в MongoDB нам понадобятся:</p>
<p>1. сам <a href="http://www.mongodb.org/downloads">MongoDB</a></p>
<p>2. <a href="http://php.net/mongo">php-драйвер монго</a></p>
<p>3. источник данных для CakePHP <a href="https://github.com/ichikaway/cakephp-mongodb">MongoDB Datasource</a></p>
<p>скачиваем плагин и кладем его в <strong>my/app/plugins/mongodb</strong> или забираем гитом</p>
<pre><code class="lang-bash">cd my/app/plugins
git clone git://github.com/ichikaway/cakephp-mongodb.git mongodb</code></pre>
<p>дальше в <strong>database.php</strong> описываем новое подключение</p>
<pre><code class="lang-php">public $mongo = array( 
    &#39;driver&#39; =&gt; &#39;mongodb.mongodbSource&#39;, 
    &#39;database&#39; =&gt; &#39;sessions&#39;, 
    &#39;host&#39; =&gt; &#39;localhost&#39;,
    &#39;port&#39; =&gt; 27017,
);</code></pre>
<p>создаем модель Session, в ней описываем схему данных (хотя монго и schemaless, но кэйк сохраняет только те поля, которые описаны в схеме)</p>
<pre><code class="lang-php">class Session extends AppModel {
    public $mongoSchema = array(
        &#39;id&#39; =&gt; array(&#39;type&#39; =&gt; &#39;string&#39;),
        &#39;data&#39; =&gt; array(&#39;type&#39; =&gt; &#39;string&#39;),
        &#39;expires&#39; =&gt; array(&#39;type&#39; =&gt; &#39;integer&#39;),
    );
}</code></pre>
<p>в <strong>core.php</strong> задаем новые параметры хранения сессий</p>
<pre><code class="lang-php">Configure::write(&#39;Session.save&#39;, &#39;database&#39;);
Configure::write(&#39;Session.model&#39;, &#39;Session&#39;);
Configure::write(&#39;Session.database&#39;, &#39;mongo&#39;);</code></pre>
<p>все получилось:</p>
<p><img src="http://evilbloodydemon.ru/blog/2011/11/cakephp-mongodb-session-storage/mongo-session.png" alt="session"></p>
]]></content>
    </entry>
    
    <entry>
        <title>Autocomplete in views — Netbeans 6.9 + CakePHP 1.3</title>
        <link href="http://evilbloodydemon.ru/blog/2010/11/netbeans-cakephp-autocomplete-view/"/>
        <updated>2010-11-09T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2010-11-09,/blog/2010/11/netbeans-cakephp-autocomplete-view/</id>
        <content type="html"><![CDATA[<p>Старый трюк с хелперами в отдельном файле для вьюх в Netbeans 6.9 работать перестал, да и в грядущем CakePHP 2.0 хелперов 
отдельными переменными во вьюхах не будет. Поэтому вот как надо поступить, чтобы наслаждаться правильным автокомплитом:</p>
<ol>
<li>Добавляем в проект файл autocomplete.php c таким вот содержимым</li>
</ol>
<pre><code class="lang-php">/**
* @property HtmlHelper $Html
* @property FormHelper $Form
* @property JsHelper $Js
* @property NumberHelper $Number
* @property PaginatorHelper $Paginator
* @property RssHelper $Rss
* @property SessionHelper $Session
* @property TextHelper $Text
* @property TimeHelper $Time
*/
class AutocompleteView extends View {
}</code></pre>
<p>2. В каждой вьюхе пишем в самом начале (да, не очень изящно, но зато работает):</p>
<pre><code>&lt;?php/* @var $this AutocompleteView */?&gt;</code></pre>
<p>3. Готово</p>
<p><img src="http://evilbloodydemon.ru/blog/2010/11/netbeans-cakephp-autocomplete-view/cakephp2x-netbeans-ide-6.png" alt="IDE screenshot"></p>
<p>UPD. для массовой вставки строчки в начало шаблонов можно воспользоваться следующей командой (linux/cygwin)</p>
<pre><code class="lang-bash">find -name &quot;*.ctp&quot; -print0 | xargs -0 sed -i &quot;1i &lt;?php /** @var \$this AutocompleteView */ ?&gt;&quot;</code></pre>
]]></content>
    </entry>
    
    <entry>
        <title>Scope Behavior</title>
        <link href="http://evilbloodydemon.ru/blog/2009/11/scope-behavior/"/>
        <updated>2009-11-16T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2009-11-16,/blog/2009/11/scope-behavior/</id>
        <content type="html"><![CDATA[<p>У меня возникла такая ситуация, что несколько сайтов с одинаковым движком используют одни и те же таблицы в одной базе данных. 
Для различения принадлежности записей в бд к сайтам используется поле <strong>site_id</strong>. Добавлять его к каждому запросу в условия 
довольно утомительно, поэтому я сделал специальный behavior для этой цели — Scope.</p>
<p>в модели (например, User) добавляем behavior и передаем ему настройки</p>
<pre><code class="lang-php">var $actsAs = array(
    &#39;Scope.Scope&#39; =&gt; array(
        &#39;field&#39; =&gt; &#39;site_id&#39;,
        &#39;value&#39; =&gt; SITE_ID,
    )
);</code></pre>
<p>теперь при вызове функций модели find или save условие будет добавляться автоматически, то есть</p>
<pre><code class="lang-php">$this-&gt;User-&gt;find(&#39;all&#39;);</code></pre>
<p>будет эквивалентно</p>
<pre><code class="lang-php">$this-&gt;User-&gt;find(&#39;all&#39;, array(
    &#39;conditions&#39; =&gt; array(
        &#39;User.site_id&#39; =&gt; SITE_ID,
    )
);</code></pre>
<p>Пользоваться, конечно, следует с осторожностью, так как некоторые операции, например updateAll и deleteAll c $cascade=false 
работают напрямую с источником данных и behavior остается не у дел (deleteAll c $cascade=true будет работать как и ожидается, с подстановкой параметров).</p>
<p>Исходный код лежит на гитхабе - <a href="http://github.com/evilbloodydemon/cakephp-scope">http://github.com/evilbloodydemon/cakephp-scope</a></p>
<p>Замечания и предложения приветствуются.</p>
]]></content>
    </entry>
    
    <entry>
        <title>Произвольная хэш функция для аутентификации в CakePHP</title>
        <link href="http://evilbloodydemon.ru/blog/2009/09/custom-hash-for-auth-cakephp/"/>
        <updated>2009-09-25T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2009-09-25,/blog/2009/09/custom-hash-for-auth-cakephp/</id>
        <content type="html"><![CDATA[<p>Разработчикам чаще приходится не создавать веб-приложения с нуля, а дорабатывать существующие или по крайней мере использовать бд, 
которая используется совместно с другими приложениями. И скорее всего в таком случае задача хранения пароля пользователя решалась 
несколько по-другому, чем по умолчанию предполагается в CakePHP. Например, хэш вычисляется по фунции</p>
<pre><code class="lang-php">sha1(md5(‘SeCrEt’ . $password . ‘KeY’)).</code></pre>
<p>Как с этим жить?</p>
<p>CakePHP хранит пароли следующим образом:
поле пароля в БД = самый стойкий из доступных хэшей(открытый пароль + секретная соль)</p>
<p>Но на наш случай у компонента Auth припасено свойство authenticate (см. <a href="http://api.cakephp.org/class/auth-component">http://api.cakephp.org/class/auth-component</a>) — 
ссылка на объект, который реализует функцию hashPassword.</p>
<p>И, собственно говоря, пример:</p>
<p>в файле app_controller.php помещаем класс для нашей хэш функции</p>
<pre><code class="lang-php">class MyHash {
    public function hashPasswords($data) {
        if (is_array($data) &amp;&amp; isset($data[‘User’])) {
            if (isset($data[‘User’][‘username’]) &amp;&amp; isset($data[‘User’][‘password’])) {
                $data[‘User’][‘password’] = sha1( md5 (‘SeCrEt’ . $data[‘User’][‘password’] . ‘KeY’) );
            }
        }
        return $data;
    }
}</code></pre>
<p>а в функции AppController::beforeFilter пишем строчку</p>
<pre><code class="lang-php">$this-&gt;Auth-&gt;authenticate = new MyHash();</code></pre>
<p>Готово!</p>
]]></content>
    </entry>
    
    <entry>
        <title>Ускорение Netbeans</title>
        <link href="http://evilbloodydemon.ru/blog/2009/09/netbeans-speedup/"/>
        <updated>2009-09-24T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2009-09-24,/blog/2009/09/netbeans-speedup/</id>
        <content type="html"><![CDATA[<p>Netbeans — самая лучшая IDE для разработки на php, но иногда несколько задумчивая. Этот недостаток исправляется некоторыми 
настройками для java-машины.</p>
<p>В /etc/netbeans.conf или в ярлык для запуска IDE нужно добавить следующие опции</p>
<pre><code class="lang-bash">-J-client -J-Xms32m -J-Xmx384m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Xverify:none -J-XX:CompileThreshold=100 -XX:+CompressedOOPS -XX:+AggressiveOpts -XX:+TieredCompilation -XX:+DoEscapeAnalysis -XX:+UseConcMarkSweepGC -J-XX:+CMSClassUnloadingEnabled -J-XX:+CMSPermGenSweepingEnabled</code></pre>
<p>Таким образом мы разрешаем использовать до 384 мб памяти, компилировать в машинный код большинство функций и оптимизировать их.</p>
<p>Результат вас приятно удивит.</p>
]]></content>
    </entry>
    
    <entry>
        <title>Netbeans CakePHP Bundle</title>
        <link href="http://evilbloodydemon.ru/blog/2009/09/netbeans-cakephp-bundle/"/>
        <updated>2009-09-10T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2009-09-10,/blog/2009/09/netbeans-cakephp-bundle/</id>
        <content type="html"><![CDATA[<p>Смастерил набор сниппетов для Netbeans, облегчающих разработку на фреймворке CakePHP.
50 для шаблонов отображения и 26 для php-кода.
Источником вдохновения послужил оригинальный бандл для маковского редактора TextMate, в котором на скринкастах так ловко набирают километры исходных кодов для торта. Возможности у сниппетов в нетбинс не такие широкие, как у текстмэйта, но я постарался максимально сохранить оргинальное поведение.</p>
<p>Дистрибутив и репозиторий лежат на <a href="http://github.com/evilbloodydemon/netbeans-cakephp-bundle/">гитхабе</a></p>
<p>Установка очень проста:</p>
<ol>
<li>Cкачать последний релиз <a href="http://github.com/evilbloodydemon/netbeans-cakephp-bundle/downloads">отсюда</a></li>
<li>Запустить нетбинс</li>
<li>Импортировать сниппеты (Tools -&gt; Options -&gt; Import)</li>
<li>Перезапустить нетбинс</li>
<li>Делать сайты в десять раз быстрее</li>
</ol>
]]></content>
    </entry>
    
    <entry>
        <title>Еще раз о Cake3</title>
        <link href="http://evilbloodydemon.ru/blog/2009/08/cake3-cakephp-again/"/>
        <updated>2009-08-06T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2009-08-06,/blog/2009/08/cake3-cakephp-again/</id>
        <content type="html"><![CDATA[<p><strong>Введение</strong></p>
<p>Да, да, я знаю. Такое <a href="http://evilbloodydemon.ru/blog/2009/07/cakephp-3.0/">уже было раньше</a>. Что я могу сказать — у меня были вопросы, а у Nate были ответы. Это первая часть, в ней рассматриваются общие вопросы о CakePHP и Cake3.</p>
<p><strong>Общие вопросы</strong></p>
<p><strong>Похоже что у CakePHP будут сразу три версии, это несомненно может запутать новых разработчиков. Я не видел официальных заявлений на этот счет, так что поправьте меня, если я не прав:
— ветка 1.3 — для всех, кто пишет под PHP4
— ветка 2.0 — для тех, кто разрабатывает новые проекты под PHP5-5.2 или для тех, кто использует PHP 5.3 и хочет проапгрейдить приложения, написанные под CakePHP 1.2
— ветка 3.0 — для приложений на PHP 5.3</strong></p>
<p>Почти верно. Как только ветка 1.3 станет стабильной, разработка версии 1.x прекратится, будут только исправляться ошибки и проблемы безопасности. Мы еще не определились с возможностью обратного портирования фич из 2.x, но это будет зависить от спроса на такую деятельность, чего не предвидится. и да, 2.0 позволит просто переносить приложения с 1.3 (его API практически не отличается от 1.2). 3.0, в свою очередь, находится в ранних стадиях разработки и не рекомендуется ни для чего, кроме экспериментов.</p>
<!-- cut -->

<p><strong>Сколько людей участвует в разработке трех новых версий Cake? Как они распределены по веткам в процентном соотношении?</strong></p>
<p>В общем у нас 16 активных разработчиков, которые работают и над кодом и над другими вещами. Над 1.3 работают 6 человек, основную работу ведут <a href="http://twitter.com/mark_story">Mark Story</a> и <a href="http://twitter.com/jperras">Joël Perras</a>. Для работ над 2.0 Larry Masters (глава проекта и корпорации cakedc.com) «одолжил» нам несколько разработчиков из своей корпорации, в частности Graham Weldon (a.k.a. <a href="http://twitter.com/predominant">Predominant</a>), который работает над полной совместимостью кода с PHP5.</p>
<p>И, наконец, я — ведущий разработчик 3.0, а Joël, Garrett, <a href="http://twitter.com/nperson">David Persso</a>n, and <a href="http://twitter.com/DarkAngelBGE">Tim</a> &amp; <a href="http://twitter.com/felixge">Felix</a> (из <a href="http://debuggable.com/">Debuggable</a>) присылают мне патчи.</p>
<p><strong>Вопросы про Cake3</strong></p>
<p><strong>Сейчас 1.3 и 2.0 имеют лицензию MIT. А у Cake3 будет лицензия BSD. Каковы причины этого и как это повлияет на разработчиков?</strong></p>
<p>Честно говоря, это никак не повлияет на разработчиков. В плане того, что вы можете делать с программным обеспечением лицензия BSD разрешает столько же, если не больше, как и MIT. Смена лицензии произошла из-за моих предпочтений: BSD более старая лицензия с более определенной историей. gwoo поддержал это решение, так как BSD включает специальный раздел, который защищает брэнд CakePHP и Cake Software Foundation от коммерческого использования, против чего мы очень возражаем. Мы рады, что люди строят бизнес на нашем коде, но использование ими наших брендов для собственного продвижения не соответствует духу того, чем мы занимаемся.</p>
<p><strong>Cake3 будет возвращать результаты запросов в виде объектов, а не в виде массивов. Я конечно могу ошибаться, но кажется ты предпочитал подход с использованием массивов? С чем связана смена позиции по этому вопросу, пришлось смириться или ты так и планировал?</strong></p>
<p>Я предпочитал массивы в определенном контексте и так как контекстом раньше являлся PHP4, и в нём были функции для работы с массивами, на которые всегда можно было рассчитывать, а нормальной объектной модели не было, то это был очевидный выбор. Сейчас мы все это переросли и нет никакой необходимости работать с массивами, что освобождает нас от множества ограничений в дизайне, с ними связанных.</p>
<p><strong>Было ли что-нибудь взято из <a href="http://groups.google.com/group/cake-php/browse_thread/thread/134b180ce477b9ef/7868d41614958d02?q=#7868d41614958d02">«топика ненависти»</a>, что ты не планировал реализовывать или хотел реализовать по другому, но пошел на поводу у сообщества?
</strong>
Возможно одна из самых значимых смен точки зрения (не только из за «топика ненависти») — это поддержка составных ключей. Простые ключи хороши тем, что их легко использовать и на низком и на высоком уровне (REST API и тд) и это здорово. Но что я осознал, это то, что часто (опять же в контексте) существуют хорошие аргументы в пользу составных ключей. И хотя мы не поддерживаем их на высоком уровне абстракции, это та вещь, которую должен поддерживать любой хорошо спроектированных слой абстракции данных.</p>
<p><strong>Предположим, я довольно хороший разработчик, который любит ковыряться в коде, присылать патчи и я начинаю проект, который будет запущен минимум через год. Было бы разумным начинать этот проект на Cake3?</strong></p>
<p>Ну, это зависит от приложения. В большинстве случаев вы просто должны были бы сделать это, потому что сейчас множество базовых вещей вроде паджинации и сессий просто не реализованы. Хотя это не имеет особого значения из-за новой структуры Cake3. Вы можете загружать классы и библиотеки довольно легко, можно даже подключить Doctrine вместо дефолтного ORM. Также большая часть API будет вам знакома, если вы уже разрабатывали на CakePHP.</p>
<p>Если у кого-то действительно есть экспериментальное приложение в разработке и он бы хотел поучаствовать в разработке Cake3, то я бы пообщался с ним по этому поводу. Самая важная вещь в этой версии — система плагинов, уже реализована и плагины обрабатываются также как и приложение и даже как ядро, так что всё взаимозаменяемо. Еще одно архитектурное отличие — большинство классов ядра может быть заменена вашими собственными.</p>
<p>С помощью этого решения мы надеемся, что когда ядро стабилизируется, вся разработка будет перенесена в плагины. Это очень важно, потому что барьер участия в маленьком проекте с ограниченным набором фич гораздо ниже. Разработка может вестись параллельно и фичи могут вносится в базовую поставку и изыматься из неё практически без влияния на код приложения.</p>
<p><strong>Что происходит с юнит-тестированием в Cake3? Похоже что SimpleTest выкинули и заменили самописным инструментом тестирования</strong></p>
<p>Опять же, это одно из тех решений, которые были сделаны за нас раньше, так как SimleTest был единственным доступным решением для PHP4. Теперь мы решили написать инструмент тестирования самостоятельно, причин тому несколько, но основные это
(а) мы хотели бы, чтобы люди могли запускать тесты сразу после установки
(б) нам нужна инфраструктура для тестов, которая заточена под наши нужды и позволяла бы нам тестировать так, как нам нужно. Причем тестировать не только ядро, но и приложения с плагинами.</p>
<p>Тем не менее, мы планируем сделать нашу систему тестирования довольно легковесной и одна из вещей, которая будет пересмотрена перед релизом — это API, скорее всего он будет более совместим с PHPUnit для того, чтобы облегчить переход для приложений с большим набором тестов.</p>
<p><strong>Система фильтов выглядит очень мило и уже обсуждалась. Какая фича Cake3 кажется вам классной, но не получила внимания?</strong></p>
<p>Самая значимая вещь, о которой я рассказывал не так много — это класс Media. Он интересен тем, что он берет на себя большую часть работы по обработке различных типов контента. Он размещается между контроллером и вью и позволяет присоединять различные обработчики для различных типов контента, причем как для ввода, так и для вывода. Это означает, что вместо того, чтобы создавать вью каждый раз, когда нужно чтобы метод контроллера отправил JSON, вы можете единожды присоединить обработчик, который будет формировать JSON из переменных, определенных в методе контроллера. А для HTML, по умолчанию определен фильтр, который отрисовывает шаблоны из каталога views/. Но даже в этом существует невероятная гибкость и теперь всё можно настраивать в одном месте.</p>
<p><strong>Я тут кое-чего с фильтрами не могу понять. Ты привязываешь фильтр к методу, но не указываешь до или после метода его нужно применять, так?
Если до, то ты просто исполняешь код до того, как вызвать return $chain-&gt;next(…). А если нужно после? Вот так вот?</strong></p>
<p><code>$ret = $chain-&gt;next($self, $params, $chain);
//my code
return $ret;</code></p>
<p>Да, именно так. Вы можете представлять себе написание фильтра как переопределение метода в подклассе, но вместо вызова родительского метода, вы вызываете $chain-&gt;next(…). У такого подхода множество преимуществ по сравнению с до- и после- фильтрами, например то, что все действия остаются в одной области видимости и вам не нужно разбивать их на два метода и беспокоится о сохранении состояния. Все операции теперь атомарны и изолированы, а код более понятен.</p>
<p>Продолжение следует</p>
<p>Эта статья — перевод. Оригинал — <a href="http://www.pseudocoder.com/archives/2009/08/05/9-questions-with-nate-abele-lead-developer-of-cakephp/">тут</a>.</p>
]]></content>
    </entry>
    
    <entry>
        <title>Cakephp 3.0</title>
        <link href="http://evilbloodydemon.ru/blog/2009/07/cakephp-3.0/"/>
        <updated>2009-07-22T00:00:00.000Z</updated>
        <id>tag:evilbloodydemon.ru,2009-07-22,/blog/2009/07/cakephp-3.0/</id>
        <content type="html"><![CDATA[<p>Сейчас очень мало информации о CakePHP 2.0 и особенно о суперновом 3.0, так что я решил провести небольшое интервью 
с Nate Abele, ведущим разработчиком CakePHP.</p>
<p><strong>Привет, Nate, CakeFest завершился и слух об анонсе CakePHP 2.0 и Cake 3 постепенно распространяется. Можешь рассказать
 в общих чертах, что будет в новых версиях?</strong></p>
<p>CakePHP 2.0 — это обновление текущей версии 1.x с переходом на строгую совместимость с PHP5, что означает, помимо 
выгоды от избавления от излишнего кода для поддержки PHP4, повышение производительности примерно на 25%.</p>
<p>Cake 3, с другой стороны, значительно отличается от текущей версии по ряду параметров. В основном тем, что с нуля переписан на PHP 5.3.</p>
<p><strong>CakePHP 2.0 будет совместим с 1.x? Мы уже пережили значительный апгрейд с 1.1 на 1.2, апгрейд на 2.0 видимо будет еще более тяжелым?</strong></p>
<p>Вообще-то, нет. CakePHP 2.0 будет практически 100% совместим на уровне API с грядущим CakePHP 1.3, который, в свою очередь, будет совместим с 1.2, за исключением некоторых устаревших (но еще работающих) методов.</p>
<p>Также, на случай миграции с 1.2 на 1.3/2.0, у нас есть инструкция, в которой будет сказано, какие небольшие изменения нужно будет внести в код существующих приложений.</p>
<p><strong>Круто, то есть будет халявный прирост производительности для всех существующих 1.2 приложений?</strong></p>
<p>Именно что.</p>
<p><strong>Хорошо, вернемся к 3.0. Расскажи-ка немного о фундаментальных изменениях в PHP как языке и о том, как они повлияли на новое ядро CakePHP.</strong></p>
<!-- cut -->

<p>Ну, для CakePHP, переход от совместимости с PHP4 прямо на php5.3 — это очень большой шаг, не только из-за новых фич 5.3, но также и из-за фич PHP5, которыми мы наконец-то сможем воспользоваться.</p>
<p>Одна из важных фич PHP5.3, которая повлияла на новую архитектуру — пространства имён. Новое ядро организовано в виде пакетов, каждый из которых состоит, в свою очередь, из других пакетов.</p>
<p>Это делает новое ядро не только модульным (т.е. его части могут быть использованы и в не-Cake приложениях), но также делает систему плагинов очень простой и мощной. Плагины теперь могут неограниченно расширять функциональность любого класса приложения, ядра и даже динамически изменять зависимости ядра.</p>
<p>Другая фича — замыкания. Замыкания — это анонимные функции (то есть функции, присвоенные свойству класса или переменной), которые могут наследовать контекст области видимости, в которой они объявлены. Это позволяет нам внедрять необходимую функциональность прямо в классы и методы, и в Cake3 мы используем их в системе фильтров.</p>
<p>В новом ядре, множество методов объектов реализуют систему фильтров, что позволяет привязывать произвольную функциональность прямо к вызовам методов: модифицировать параметры и возвращаемые значения.</p>
<p>Это очень мощная штука, она позволяет легко реализовать такую функциональность как кэширование или логгирование, так как она может быть применена ненавязчиво, без необходимости для целевого класса знать, как делать все эти штуки.</p>
<p>Одна из вещей, с которой мы боремся как разработчики — слишком тесное взаимодествие классов из-за того, что каждый знает о механизме работы другого.
С помощью системы фильтров мы можем сохранять классы полность изолированными, но по-прежнему имеющими возможноть взаимодействовать не зная ничего друг о друге.
Другая отличная фича этой системы — стандартный интерфейс, поэтому все фильтры могут быть применены одинаковым образом. Так что если вы знаете, как писать фильтры для одного класса, то вы знаете, как это делать для них всех.</p>
<p><strong>Можно пример?</strong></p>
<p>Предположим, я хочу лог всех запросов к БД. В 1.2 классы работы с БД записывают запросы, исполняют их и возвращают список, когда их попросят. Хотя классы работы с БД должны работать только с БД, они не должны ничего знать ни о каких логах.</p>
<p>В Cake3 я могу сделать так:</p>
<pre><code class="lang-php">applyFilter(‘_execute’, function($self, $params, $chain) {
    $out = fopen(‘php://stderr’);
    fwrite($out, $params[&#39;sql&#39;]);
    fclose($out);
    return $chain-&gt;next($self, $params, $chain);
});</code></pre>
<p>Здесь я получаю объект БД из класса Connections (это эквивалент вызова ConnectionManager::getDataSource() в 1.2) и присоединяю фильтр, который перехватывает метод _execute().
После этого, я мог бы отправить вывод в класс логгинга, но сейчас я просто хочу вывести запросы в лог ошибок, чтобы быстренько посмотреть что да как.
Фильтр будет применен каждый раз, когда запрос отправится в БД, таким образом все запросы будут записаны, как я и хотел.</p>
<p><strong>Получается, что фильтры можно применить только к объекту, а не к классу?</strong></p>
<p>Ну вообще-то пример — это лишь маленькая часть мощи системы фильтров.
Для инстанцируемых классов — да, обычно фильтры применяются по необходимости. Хотя, фреймворк активно разрабатывается, и мы столкнулись с необходимостью конфигурировать все объекты определенного типа, так что в будущем будет возможно и это.</p>
<p>Кроме того, в ядре появится новый класс — Collection. Этот класс похож на массив, но с дополнительными полезностями, наподобие вызова метода у всех содержащихся в нем объектах при вызове метода у самого класса коллекции. Таким образом, возможно применить фильтр у множества объектов сразу используя такую технику.</p>
<p><strong>Ты упомянул класс коллекций, а вот я слышал, что модели подвергнуться наибольшим изменениям в 3.0. Не раскроешь планы насчет этого?</strong></p>
<p>Да, в чем-то модели поменяются, а в чем-то — нет. Например, записи по-прежнему будут получаться чем-то наподобие $posts = Post::find(«all»);
Это и все связанное с моделями должно быть сразу же понятно любому, кто сейчас работает с кэйком.</p>
<p>Но внутри, модели будут полностью новыми. Теперь они будут взаимодействовать с источниками данных через ограниченный набор методов с помощью объектов Query. Эти объекты очень полезны, так как они позволяют нам инкапсулировать большую часть работы по генерации запросов, которая раньше была разбросана по нескольким разным классам. Кроме того, так как query-объекты работают с уровнем данных и так как объекты ядра могут быть заменены пользовательскими, то пользователи могут легко модифицировать и расширять синтаксис SQL, поддерживаемый кэйком.</p>
<p>С помощью упоминавшегося класса коллекций, результаты запроса стали объектами, причем объектами стали не только записи, но и наборы записей. Объект RecordSet, потомок Collection, похож на массив в том смысле, что можно пройтись по нему foreach сотоварищи. Но он также имеет и значительные улучшения, наподобие ленивой подгрузки записей по мере надобности.
Эта его особенность отражает архитектурное решение — во всех частях фрэймворка, где это только возможно, используется новая более объектно-ориентированная архитектура, причем ленивая.</p>
<p>То есть любая загрузка или обработка не произойдет до тех пор, пока реально не понадобиться. Классы не загружаются, пока не начнут использоваться, маршруты не компилируются, пока не будут запрошены, результаты запросов не извлекаются пока вы не начнете их обрабатывать.
Вместе с множеством других архитектурных улучшений это делает новое ядро очень эффективным.</p>
<p><strong>Если записи теперь объекты, получается что CakePHP получит полноценную реализацию ActiveRecord?</strong></p>
<p>Ну да. С новой фичей PHP5.3 — Late Static Binding мы можем обращаться к статическим классам, как показано выше.</p>
<p><strong>Хорошо. Моя личная цель — прекратить испльзовать реляционные БД для неструктурированных данных в 2009 году. В Cake3 будет поддержка чего-нибудь такого?</strong></p>
<p>Определенно. С упрощенным интерфейсом DataSource и менее строгими требованиями к схеме данных, моделирование нереляционных данных стало значительно легче. Новая система моделей также более гибкая в плане определения связей между моделями. С помощью query-объектов вы можете реализовать флаги и выражения, специфичные для хранилища данных, с которым вы работаете.</p>
<p><strong>Невероятно! И когда же мы получим все эти крутанские штуки? Есть какой-нибудь план по выпуску Cake3?</strong></p>
<p>Хотя код доступен непосредственно на code.cakephp.org, трудно сказать когда будет официальный релиз. Но ожидайте значительного объема работы в ближайшие месяцы.</p>
<p><strong>У Cake3 появились новые фичи и новые требования к серверной части. Что если сравнить его с такими фремворками, как Ruby on Rails, Django и тд?</strong></p>
<p>Ну, миграция с PHP 5.2 на 5.3 быстрая и безболезненная, так как в области существующих фич мало что изменилось. Как всегда PHP — это самая простая в использовании и самая простая в развертывании платформа в вебе. Разместить сайт — это всего лишь скопировать файлы в корень сайта.</p>
<p>Поддержка PHP приложений всегда была простой и в плане производительности PHP всегда был на высоте. С его архитектурой вам не приходится беспокоиться об управлении памятью, дедлоками или проблемами с инфраструктурой при масштабировании.</p>
<p>Это перевод, оригинал — здесь</p>
<p>Ссылки:
CakePHP 2.0 — <a href="http://code.cakephp.org/cakephp2">http://code.cakephp.org/cakephp2</a>
Cake3 — <a href="http://code.cakephp.org/cake3">http://code.cakephp.org/cake3</a></p>
]]></content>
    </entry>
    
</feed>