вторник, 24 февраля 2015 г.

Changelog ExampleCMS

Новая порция изменений в проекте ExampleCMS:
  1. Создан новый тип точки входа (CliApplication) для консольных приложений, стало возможным разрабатывать консольные приложения, так же удобно как и веб-приложения.
  2. Наброски комманд help и install для консольного приложения.
  3. Глобальное применение ролей для ограничения прав
  4. Возрат паттерна null-объект для не существующих моделей
  5. В контексте появился метод для получения текущего пользователя
Ревизии:
https://bitbucket.org/cheevauva/examplecms/commits/81b2c4a14bb873e476ab86a4af85a5868908229c

https://bitbucket.org/cheevauva/examplecms/commits/4e9ed8102c6fc1234e9d6a7d3429196810d8acc3

https://bitbucket.org/cheevauva/examplecms/commits/2c25928e0f20ee2250a80dd9bb6704719d2b94f7

Внедрение зависимостей

В этом посте я опишу что такое внедрение зависимостей, зачем оно нужно и покажу на рабочем примере как его использовать.

Начнем с самого понятия, википедия говорит нам следущее:
Цитата
Внедрение зависимости (англ. Dependency injection, DI) — процесс предоставления внешней зависимости программному компоненту.


Проще говоря "внедрение зависимостей" это когда объект не знает о своих зависимостях ничего кроме интерфейса . Но стоит заметить что это самый идеальный вариант, реализация же предполагает возможность ряда допущений.

Классификация

Внедрение зависимостей делится на 4 типа:
1. Внедрение через конструктор (constructor injection)
2. Внедрение через сеттеры (setter injection)
3. Внедрение через свойства (property injection)
4. Внедрение через контейнер (service locator)

Для своих проектов я использую property injection и поэтому в этом посте буду расказывать про него.

Property injection, это когда у вас есть экземляр объекта и у него есть публичное свойство в котором предположительно должен лежать другой объект(зависимость), необходимый для работы этому объекту.

Простой пример

Что бы стало понятно, приведу простой пример внедрения зависимости через свойство.
<?php

class Object2
{
    
}

class Object1
{
    /**
     * @var Object2
     */
    public $object2;
}

$object1 = new Object1;
$object1->object2 = new Object2;


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

Внедрение зависимостей через конфигурацию

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

Контейнер зависимостей будет обладать следующими функциями:
1. Получение и хранение объектов
2. Внедрение зависимосте для объектов

Пример простейшего контейнера зависимостей:
<?php

class Object1
{

    /**
     * @var Object2
     */
    public $object2;

}

class Object2
{
    /**
     * @var Object1
     */
    public $object1;
    
    /**
     * @var Object3
     */
    public $object3;
}

class Object3
{
    /**
     * @var DIContainer
     */
    public $container;
}

class DIContainer
{

    protected $objects = array();
    protected $injections = array();

    public function __construct()
    {
        $this->objects['DIContainer'] = $this;
    }

    public function getObject($className)
    {
        if (!empty($this->objects[$className])) {
            return $this->objects[$className];
        }
        
        $object = $this->objects[$className] = new $className;
        
        foreach ($this->injections[$className] as $property => $injectClassName) {
            $object->{$property} = $this->getObject($injectClassName);
        }
        
        return $object;
    }

    public function setInjections(array $injections)
    {
        $this->injections = $injections;
    }

}

$injections = array(
    'Object1' => array(
        'object2' => 'Object2',
    ),
    'Object2' => array(
        'object1' => 'Object1',
        'object3' => 'Object3',
    ),
    'Object3' => array(
        'container' => 'DIContainer',
    )
);

$container = new DIContainer;
$container->setInjections($injections);
/* @var $object1 Object1 */
$object1 = $container->getObject('Object1');

var_dump($object1->object2->object3->container);


Если запустить пример, то можно увидеть что при получении Object1, автоматически внедрятся зависимости не только в Object1, но и в зависимости зависимостей. Это существенный плюс, ведь нам не придется задумываться о том что какой-то зависимости не хватит своих зависимостей, у нас есть гарантия что все зависимосте зависимостей будут загружены если они были заданы в конфигурационном массиве.

Для чего же нужно внедрение зависимостей

1. Перечисление зависимостей всего проекта в одном месте.
2. Автоматическая загрузка зависимостей у зависимостей.
3. Слабая связаннасть
4. Исключение статики из кода

Банальные советы по улучшению читаемости кода

Оформление сложных условий

Плохо:
<?php 
function someFuction($a, $b, $c)
{
    if (!empty($a) && !empty($b) && !empty($c)) {
        // do something
    }
}


Хорошо:
<?php
function someFuction($a, $b, $c)
{
    if (empty($a)) {
        return;
    }
    
    if (empty($b)) {
        return;
    }
    
    if (empty($c)) {
        return;
    }    
    
    // do something
}


Сокрашение количества вложенных условий

Плохо:
<?php
function someFunction($a, $b, $c)
{
    if ($a) {
        if ($b) {
            if ($c) {
                // do something 2
            }
        }
        // do something 1
    }
}


Хорошо:
<?php
function someFunction($a, $b, $c)
{
    if (!$a) {
        return;
    }
    
    // do something 1
    if (!$b) {
        return;
    }
    
    if (!$c) {
        return;
    }
    
    // do something
}

Девелоперская машина на основе Ubuntu

В этом посте я раскажу как легко задеплоить девелоперские интрументы(для php) на только что установленную Ubuntu 14.

Для этого нам потребуется:
1. Ubuntu, с доступом к sudo - 1 штука
2. Наличие интернета
3. Прямые руки - 2 штуки

Список софта который будет установлен после всех действий:
1. mysql server/client
2. apache 2
3. php 5.5
3. phpmyadmin
4. mercurial
5. java
6. netbeans

Откройте эмулятор терминала и следуйте инструкциям.

Приступим:

Для начала установим mysql сервер, для этого введем в конслоли команду
sudo apt-get install mysql-server

будет запрошен пароль sudo пользователя, после ввода, начнется поиск пакета, если пакет найдет, то
вам будет представлена информация о скачиваемых и устанавливаемых пакетах. Подтверждаем.
Идет закачка, настройка. После появляется диалог где вас попросят ввести пароль к учетной записи mysql.
После ввода, стартует сервер. Всё. У вас есть mysql server.

Следующий шаг это установка phpmyadmin, вводим команду
sudo apt-get install phpmyadmin

Данный пакет содержит зависимостями php и apache 2/lighttpd(по выбору), а также mysql-client.
Посде того как все пакеты скачаются и настроятся, вам будет предложено ввести пароль для пользоватьля базы данных,
вводи пароль что указывали выше.

Установка lamp завершена. Если вы перейдете по странице http://localhost/, то убидитесь в этом.

Следуйщий шаг это установка IDE, в нашем случае netbeans. Вводим в терминале команду
sudo apt-get install netbeans

ждем пока все установится и удалится, будут установлены java и netbeans.
так как Netbeans будет установлен для работы с java, а мы с ней не собираемся работать, то удаляем пакет
sudo apt-get remove netbeans


После чего идем на сайт https://netbeans.org/downloads/ и скачиваем пакет для PHP. После скачивания не забудьте сделать файл исполняемым.

Следущий шаг установка контроля версий, в терминале вводим следущую команду:
sudo apt-get install mercurial

Пакеты скачаются и установятся.

После этого вам останется только настроить существующие пакеты и права на файлы и система будет готова к обильному кодингу.

Проверка подсветки синтаксиса

<?php

namespace ExampleCMS\Action\Cli;

class Install implements \ExampleCMS\Contract\Action
{

    public function execute()
    {
        $repository = $this->context->getRepository();
        
        $query = $repository->getQuery('CreateTable');
        $query->config = $this->config;
        $query->execute();
        die;
        
        $this->config->set('base.basePath', readline('Относительный путь до файла index.php:'));
        $this->config->set('base.db', array(
            'type' => 'sqlite',
            'path' => 'cache/database.db',
        ));


    }

}

Подсветку синтаксиса оказывается возможно подключить, подробнее тут http://stackoverflow.com/questions/10335463/how-to-setup-syntax-highlighter-on-blogger