No PHP, atributos da classe são sempre chamadas de *propriedades*. Esses atributos fazem parte da definição da classe e são usadas para representar o estado de uma instância da classe (para diferenciar uma instância da classe de outra). Na prática, muitas vezes você pode querer lidar com a leitura ou a escrita de propriedades de maneiras especiais. Por Exemplo, você pode querer trimar uma string sempre que for atribuído um valor para a propriedade `label`. Você *poderia* usar o código a seguir para realizar esta tarefa:
```php
$object->label = trim($label);
```
A desvantagem do código acima é que você teria que chamar `trim ()` em todos os lugares onde você definir a propriedade `label` no seu código. Se, no futuro, a propriedade `label` receber um novo requisito, tais como a primeira letra deve ser capitalizado, você teria que modificar novamente todos os pedaços de código que atribui um valor para a propriedade `label`. A repetição de código leva a erros, e é uma prática que você deve evitar sempre que possível.
Para resolver este problema, Yii introduz uma classe base chamada [[yii\base\Object]] que suporta definições de propriedades baseadas nos métodos *getter* e *setter* da classe. Se uma classe precisa dessa funcionalidade, ela deve estender de [[yii\base\Object]], ou de uma classe-filha.
> Informação: Quase todas as classes nativas (core) do framework Yii estendem de [[yii\base\Object]] ou de uma classe-filha. Isto significa que sempre que você vê um método *getter* ou *setter* em uma classe nativa (core), você pode usá-lo como uma propriedade.
Um método getter é um método cujo nome inicia com a palavra `get`; um método setter inicia com `set`.
O nome depois do prefixo `get` ou `set` define o nome da propriedade. Por exemplo, um getter `getLabel()` e/ou um setter `setLabel()` define a propriedade chamada `label`, como mostrado no código a seguir:
```php
namespace app\components;
use yii\base\Object;
class Foo extends Object
{
private $_label;
public function getLabel()
{
return $this->_label;
}
public function setLabel($value)
{
$this->_label = trim($value);
}
}
```
(Para ser claro, os métodos getter e setter criam a propriedade `label`, que neste caso internamente refere-se ao atributo privado chamado `_label`.)
Propriedades definidas por getters e setters podem ser usados como atributos da classe. A principal diferença é que quando tal propriedade é iniciada para leitura, O método getter correspondente será chamado; quando a propriedade é iniciada atribuindo um valor, o método setter correspondente será chamado. Por Exemplo:
```php
// equivalent to $label = $object->getLabel();
$label = $object->label;
// equivalent to $object->setLabel('abc');
$object->label = 'abc';
```
A propriedade definida por um método getter sem um método setter é *somente de leitura*. Tentando atribuir um valor a tal propriedade causará uma exceção [[yii\base\InvalidCallException|InvalidCallException]]. Semelhantemente, uma propriedade definida por um método setter sem um método getter é *somente de gravação *, e tentar ler tal propriedade também causará uma exceção. Não é comum ter propriedade *somente de gravação*.
Existem várias regras especiais para, e limitações sobre, as propriedades definidas via getters e setters:
* Os nomes dessas propriedades são *case-insensitive*. Por exemplo, `$object->label` e `$object->Label` são a mesma coisa. Isso ocorre porque nomes de métodos no PHP são case-insensitive.
* Se o nome de uma tal propriedade é o mesmo que um atributo da classe, esta última terá precedência. Por exemplo, se a classe `Foo` descrita acima tiver um atributo `label`, então a atribuição `$object->label = 'abc'` afetará o *atributo* 'label'; esta linha não executaria `setLabel()` método setter.
* Essas propriedades não suportam visibilidade. Não faz nenhuma diferença para a definição dos métodos getter ou setter se a propriedade é pública, protegida ou privada.
* As propriedades somente podem ser definidas por getters e/ou setters *não estáticos*. Os métodos estáticos não serão tratados da mesma maneira.
Voltando para o problema descrito no início deste guia, em vez de chamar `trim()` em todos os lugares que um valor for atribuído a `label`, `trim()`Agora, só precisa ser invocado dentro do setter `setLabel()`.E se uma nova exigência faz com que seja necessário que o 'label' seja inicializado capitalizado, o método `setLabel()` pode rapidamente ser modificado sem tocar em nenhum outro código. Esta única mudança afetará de forma global cada atribuição à propriedade `label`.
Во время записи и чтения кук через компоненты `request` и `response`, как будет показано в двух последующих подразделах, фреймворк предоставляет автоматическую валидацию, которая обеспечивает защиту кук от модификации на стороне клиента. Это достигается за счет подписи каждой куки секретным ключом, позволяющим приложению распознать куку, которая была модифицирована на клиентской стороне. В таком случае кука НЕ БУДЕТ доступна через свойство [[yii\web\Request::cookies|cookie collection]] компонента `request`.
Во время записи и чтения кук через компоненты `request` и `response`, как будет показано в двух последующих подразделах, фреймворк предоставляет автоматическую валидацию, которая обеспечивает защиту кук от модификации на стороне клиента. Это достигается за счет подписи каждой куки секретным ключом, позволяющим приложению распознать куку, которая была модифицирована на клиентской стороне. В таком случае кука НЕ БУДЕТ доступна через свойство [[yii\web\Request::cookies|cookie collection]] компонента `request`.
> Замечение: Валидация кук защищает только от их модификации. Если валидация не была пройдена, получить доступ к кукам все еще можно через глобальную переменную `$_COOKIE`. Это связано с тем, что дополнительные пакеты и библиотеки могут манипулировать куками без вызова валидации, которую обеспепичает Yii.
> Замечение: Валидация кук защищает только от их модификации. Если валидация не была пройдена, получить доступ к кукам все еще можно через глобальную переменную `$_COOKIE`. Это связано с тем, что дополнительные пакеты и библиотеки могут манипулировать куками без вызова валидации, которую обеспечивает Yii.
По-умолчанию валидация кук включена. Её можно отключить, установив свойство [[yii\web\Request::enableCookieValidation]]
По-умолчанию валидация кук включена. Её можно отключить, установив свойство [[yii\web\Request::enableCookieValidation]]
в false, однако мы настоятельно не рекомендуем это делать.
в false, однако мы настоятельно не рекомендуем это делать.
* [[yii\filters\AccessRule::verbs|verbs]]: задаёт http метод (например `GET`, `POST`) соответствующий правилу.
Сравнение регисронезависимо.
Сравнение регисронезависимо.
* [[yii\filters\AccessRule::matchCallback|matchCallback]]: задаёт PHP колбек, который вызывается для определения,
* [[yii\filters\AccessRule::matchCallback|matchCallback]]: задаёт PHP колбек, который вызывается для определения,
@ -112,7 +112,7 @@ IP адрес может содержать `*` в конце, так чтоб
* [[yii\filters\AccessRule::denyCallback|denyCallback]]: задаёт PHP колбек, который будет вызван если доступ будет
* [[yii\filters\AccessRule::denyCallback|denyCallback]]: задаёт PHP колбек, который будет вызван если доступ будет
запрещён при вызове этого правила.
запрещён при вызове этого правила.
Ниже покзан пример, который показывает использование опции `matchCallback`, которая позволяет писать произвольную
Ниже покзан пример, показывающий использование опции `matchCallback`, которая позволяет писать произвольную
логику проверки доступа:
логику проверки доступа:
```php
```php
@ -152,28 +152,28 @@ class SiteController extends Controller
---------------------------------------
---------------------------------------
Управление доступом на основе ролей (RBAC) обеспечивает простой, но мощьный централизованный контроль доступа.
Управление доступом на основе ролей (RBAC) обеспечивает простой, но мощьный централизованный контроль доступа.
Пожалуйста обратитесь к [Wikipedia](http://en.wikipedia.org/wiki/Role-based_access_control) для получения более
Пожалуйста обратитесь к [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A3%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BE%D0%BC_%D0%BD%D0%B0_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B5_%D1%80%D0%BE%D0%BB%D0%B5%D0%B9)
подробного сравнения RBAC с другими более традиционными системами контроля доступа.
для получения более подробного сравнения RBAC с другими, более традиционными, системами контроля доступа.
Yii реализует общую иерархическую RBAC, следуя [NIST RBAC model](http://csrc.nist.gov/rbac/sandhu-ferraiolo-kuhn-00.pdf).
Yii реализует общую иерархическую RBAC, следуя [NIST RBAC model](http://csrc.nist.gov/rbac/sandhu-ferraiolo-kuhn-00.pdf).
Обеспечивается функциональность RBAC через [[yii\rbac\ManagerInterface|authManager]] [компонент приложения](structure-application-components.md).
Обеспечивается функциональность RBAC через [компонент приложения](structure-application-components.md) [[yii\rbac\ManagerInterface|authManager]].
Использование RBAC состоит из двух частей. Первая часть - это создание RBAC данных авторизации, и вторая часть это
Использование RBAC состоит из двух частей. Первая часть - это создание RBAC данных авторизации, и вторая часть - это
использование данных авторизации для проверки доступа в том месте где это нужно.
использование данных авторизации для проверки доступа в том месте, где это нужно.
Для облегчения последующего описания, мы сначала введём некоторые основные понятия RBAC.
Для облегчения последующего описания, мы сначала введём некоторые основные понятия RBAC.
### Основные концепции
### Основные концепции
Роль представляет собой набор *permissions* (разрешения) (например создание сообщения, обновление сообщения).
Роль представляет собой набор разрешений (*permissions*) (например создание сообщения, обновление сообщения).
Роль может быть назначена на одного или многих пользователей. Чтобы проверить, имеет ли пользователь указанные
Роль может быть назначена на одного или многих пользователей. Чтобы проверить, имеет ли пользователь указанные
разрешения, мы можем проверить назначена ли пользователю роль, которая содержит данное разрешение.
разрешения, мы должны проверить назначена ли пользователю роль, которая содержит данное разрешение.
С каждой ролью или разрешением может быть связано *rule* (правило). Правило представляет собой кусок кода, который будет
С каждой ролью или разрешением может быть связано правило (*rule*). Правило представляет собой кусок кода, который будет
выполняться в ходе проверки доступа для определения может ли быть применена соответствующая роль или разрешение
выполняться в ходе проверки доступа для определения может ли быть применена соответствующая роль или разрешение
к текущему пользователю. Например, разрешение "обновление поста" может иметь правило, которое проверяет является ли
к текущему пользователю. Например, разрешение "обновление поста" может иметь правило, которое проверяет является ли
текущий пользовател автором поста. Во время проверки доступа, если пользователь не является автором поста, он/она будет
текущий пользователь автором поста. Во время проверки доступа, если пользователь не является автором поста, он/она будет
считаться не имеющими разрешения "обновление поста".
считаться не имеющими разрешения "обновление поста".
И роли и разрешения могут быть организованы в иерархию. В частности роль может содержать другие роли или разрешения; и
И роли и разрешения могут быть организованы в иерархию. В частности роль может содержать другие роли или разрешения; и
@ -183,10 +183,11 @@ Yii реализует общую иерархическую RBAC, следуя
### Настройка RBAC Manager
### Настройка RBAC Manager
Перед определением авторизационных данных и проверкой прав доступа, мы должны настроить компонент приложения [[yii\base\Application::authManager|authManager]].
Перед определением авторизационных данных и проверкой прав доступа, мы должны настроить компонент приложения
Yii предоставляет два типа менеджеров авторизации: [[yii\rbac\PhpManager]] и [[yii\rbac\DbManager]]. Первый использует
[[yii\base\Application::authManager|authManager]]. Yii предоставляет два типа менеджеров авторизации:
файл с PHP скриптом для хранения данных авторизации, второй сохраняет данные в базе данных. Вы можете использовать
[[yii\rbac\PhpManager]] и [[yii\rbac\DbManager]]. Первый использует файл с PHP скриптом для хранения данных авторизации,
первый если ваше приложение не требует слишком динамичным управлением ролями и разрешениями.
второй сохраняет данные в базе данных. Вы можете использовать первый если ваше приложение не требует слишком динамичного
управления ролями и разрешениями.
#### настройка authManager с помощью `PhpManager`
#### настройка authManager с помощью `PhpManager`
@ -225,14 +226,14 @@ return [
];
];
```
```
`DbManager` использует четыре таблицы базы данных для хранения данных:
`DbManager` использует четыре таблицы для хранения данных:
- [[yii\rbac\DbManager::$itemTable|itemTable]]: таблица для хранения авторизационных элементов. По умолчанию "auth_item".
- [[yii\rbac\DbManager::$itemTable|itemTable]]: таблица для хранения авторизационных элементов. По умолчанию "auth_item".
- [[yii\rbac\DbManager::$itemChildTable|itemChildTable]]: таблица для хранения иерархии элементов. По умолчанию "auth_item_child".
- [[yii\rbac\DbManager::$itemChildTable|itemChildTable]]: таблица для хранения иерархии элементов. По умолчанию "auth_item_child".
- [[yii\rbac\DbManager::$assignmentTable|assignmentTable]]: таблица для хранения назначений элементов авторизации. По умолчанию "auth_assignment".
- [[yii\rbac\DbManager::$assignmentTable|assignmentTable]]: таблица для хранения назначений элементов авторизации. По умолчанию "auth_assignment".
- [[yii\rbac\DbManager::$ruleTable|ruleTable]]: таблица для хранения правил. По умолчанию "auth_rule".
- [[yii\rbac\DbManager::$ruleTable|ruleTable]]: таблица для хранения правил. По умолчанию "auth_rule".
Прежде чем вы начнёте его использовать вам нужно создать эти таблицы в базе данных. Чтобы сделать это,
Прежде чем вы начнёте использовать этот менеджер, вам нужно создать таблицы в базе данных. Чтобы сделать это,
вы можете использовать миграцию хранящуюся в файле `@yii/rbac/migrations`:
вы можете использовать миграцию хранящуюся в файле `@yii/rbac/migrations`:
Обратите внимание, что обработка с помощью HtmlPurifier довольно тяжела, так что неплохо бы задуматься о кешировании.
Как избежать CSRF
-----------------
CSRF - это аббревиатура для межсайтинговой подмены запросов. Идея заключается в том, что многие приложения предполагают,
что запросы, приходящие от браузера, отправляются самим пользователем. Это может быть неправдой.
Например, сайт `an.example.com` имеет URL `/logout`, который, используя простой GET, разлогинивает пользователя. Пока
это запрос выполняется самим пользователем - всё в порядке, но в один прекрасный день злоумышленники размещают код
`<img src="http://an.example.com/logout">` на форуме с большой посещаемостью. Браузер не делает никаких отличий
между запросом изображения и запросом страницы, так что когда пользователь откроет страницу с таким тегом `img`, браузер отправит GET запрос на указанный адрес, и пользователь будет разлогинен.
Вот основная идея. Можно сказать, что в разлогировании пользователя нет ничего серьёзного, но с помощью этой уязвимости, можно выполнять опасные операции. Представьте, что существует страница http://an.example.com/purse/transfer?to=anotherUser&amout=2000, обращение к которой с помощью GET запроса, приводит к перечислению 2000 единиц валюты со счета авторизированного пользователя на счет пользователя с логином anotherUser. Учитывая, что браузер для загрузки контента отправляет GET запросы, можно подумать, что разрешение на выполнение такой операции только POST запросом на 100% обезопасит от проблем. К сожалению. это не совсем правда. Учитывайте, что вместо тега <img>, злоумышленник может внедрить JavaScript код, который будет отправлять нужные POST запросы на этот URL.
Для того, чтоб избежать CSRF вы должны всегда:
1. Следуйте спецификациям HTTP, например запрос GET не должен менять состояние приложения.
2. Держите защиту CSRF в Yii включенной.
Как избежать нежелательного доступа к файлам
--------------------------------------------
По умолчанию, webroot сервера указывает на каталог `web`, где лежит `index.php`. В случае использования виртуального
хостинга, это может быть недостижимо, в конечном итоге весь код, конфиги и логи могут оказаться в webroot сервера.
Если это так, то нужно запретить доступ ко всему, кроме директории `web`. Если на вашем хостинге такое невозможно,
рассмотрите возможность смены хостинга.
Как избежать отладочной информации и утилит в продуктиве
@ -73,4 +73,4 @@ There are several special rules for, and limitations on, the properties defined
* These properties do not support visibility. It makes no difference to the defining getter or setter method if the property is public, protected or private.
* These properties do not support visibility. It makes no difference to the defining getter or setter method if the property is public, protected or private.
* The properties can only be defined by *non-static* getters and/or setters. Static methods will not be treated in the same manner.
* The properties can only be defined by *non-static* getters and/or setters. Static methods will not be treated in the same manner.
Returning back to the problem described at the beginning of this guide, instead of calling `trim()` everywhere a `label` value is assigned, `trim()` now only needs to be invoked within the setter `setLabel()`. And if a new requirement makes it necesary that the label be initially capitalized, the `setLabel()` method can quickly be modified without touching any other code. The one change will universally affect every assignment to `label`.
Returning back to the problem described at the beginning of this guide, instead of calling `trim()` everywhere a `label` value is assigned, `trim()` now only needs to be invoked within the setter `setLabel()`. And if a new requirement makes it necessary that the label be initially capitalized, the `setLabel()` method can quickly be modified without touching any other code. The one change will universally affect every assignment to `label`.
@ -139,10 +139,9 @@ from a user browser are made by the user himself. It could be false.
For example, `an.example.com` website has `/logout` URL that, when accessed using a simple GET, logs user out. As long
For example, `an.example.com` website has `/logout` URL that, when accessed using a simple GET, logs user out. As long
as it's requested by the user itself everything is OK but one day bad guys are somehow posting
as it's requested by the user itself everything is OK but one day bad guys are somehow posting
`<img src="http://an.example.com/logout">` on a forum user visits frequently. Browser doesn't make any difference between
`<img src="http://an.example.com/logout">` on a forum user visits frequently. Browser doesn't make any difference between
requesting an image or requesting a page so when user opens a page with such `img` tag he's being logged out from
requesting an image or requesting a page so when user opens a page with such `img` tag, the browser will send the GET request to that URL, and the user will be logged out from `an.example.com`.
`an.example.com`.
That's the basic idea. One can say that logging user out is nothing serious. Well, sending POST isn't much trickier.
That's the basic idea. One can say that logging user out is nothing serious, but bad guys can do much more, using this idea. Imagine that some website has an URL `http://an.example.com/purse/transfer?to=anotherUser&amout=2000`. Accessing it using GET request, causes transfer of $2000 from authorized user account to user `anotherUser`. We know, that browser will always send GET request to load an image, so we can modify code to accept only POST requests on that URL. Unfortunately, this will not save us, because an attacker can put some JavaScript code instead of `<img>` tag, which allows to send POST requests on that URL.
Следующие шаги не обязательны, если вы хотите работать только с переводом или документацией.
- выполните `composer update` для установки зависимостей (если [composer у вас установлен глобально](https://getcomposer.org/doc/00-intro.md#globally)).
- выполните `php build/build dev/app basic` для клонирования базового приложения и установки его зависимостей.
Эта команда установит сторонние пакеты composer обычным образом, но создаст ссылку с репозитория yii2 на только
что загуженный репозиторий. Таким образом у вас будет только один экземпляр кода.
При необходимости делаем тоже самое для приложения advanced: `php build/build dev/app advanced`.
Данная команда также может быть использована для обновления зависимостей, внутри она использует `composer update`.
**Теперь у нас есть рабочая площадка для экспериментов с Yii 2.**
Следующие шаги не обязательны.
### Модульные тесты
Вы можете выполнить модульные тесты с помощью команды `phpunit` в корневой директории приложения. Если у вас phpunit
не установлен глобально, вы можете запустить `php vendor/bin/phpunit`.
Некоторые тесты требуют дополнительно установки и настройки баз данных. Вы можете создать `tests/data/config.local.php`
для переопределения настроек, которые определены в `tests/data/config.php`.
Вы можете ограничить тестирование группой тестов, с которой вы сейчас работаете, например запускать только тесты для
валидаторов и redis. Вы можете получить список доступных групп выполнив `phpunit --list-groups`.
### Расширения
Для работы над расширениями вы можете склонировать репозиторий расширения. Мы сделали команду, которая поможет вам
сделать это:
```
php build/build dev/ext <extension-name>
```
где `<extension-name>` это имя расширения, например `redis`.
Если вы хотите протестировать расширение в одном из шаблонов приложений, просто добавьте его в `composer.json`
приложения, например добавив `"yiisoft/yii2-redis": "*"` в секцию `require` базового приложения.
Запустите `php build/build dev/app basic` для установки расширения и его зависимостей и создания символической
ссылки на `extensions/redis` так чтоб вы работали не папке вендорных пакетов composer, а напрямую в репозитории yii2.
Работа над багами и новыми функциями
------------------------------------
Приготовив вашу среду разработки вы можете начать работу над исправлением багов и разработкой новых функций.
### 1. Убедитесь, что issue создана для того, над чем вы работаете, если потребуется много времени для исправления
Все новые функции и баги должны быть связаны с issue для того, чтоб иметь единое место для обсуждения и документирования.
Пртратьте несколько минут на поиск существующей issue, которая соответствует вашим изменениям. Если вы найдёте её в
списке, то пожалуста откоментируйте, что вы начали над ней работать. Если вы не нашли нужной issue пожалуйста
[создайте новую issue](report-an-issue.md) или создайте сразу запрос на изменения если это простые изменения.
Это позволит команде проверить ваше предложение, и обеспечивать соответствующую обратную связь.
> Для небольших изменении или документации, или простых исправлений, вам нет необходимости создавать issue,
запрос на изменения в этом случае подходит лучше.
### 2. Вытягивание последнего кода из основного репозитория Yii
```
git fetch upstream
```
Вы должны начинать с этого действия работу над каждым новым предложением, убеждайтесь что вы работаете над самой
последней версией кода.
### 3. Создание новой ветки для ваших изменений, основанных на текущем мастере Yii
> Это очень важно, так как вы не сможете отправлять более одного запроса на изменения из вашего репозитория, если
будете использовать ветку master.
Каждая отдельное исправление или изменение должно разрабатываться в своей ветке. Имя ветки должно быть описательным
и начинаться с номера issue, с которым связан ваш код. Если вы не исправляете какой-то конкретный issue, просто
пропустите номер.
Например:
```
git checkout upstream/master
git checkout -b 999-name-of-your-branch-goes-here
```
### 4. Делайте свою магию, пишите ваш код
Убедитесь, что он работает :)
Модульные тесты всегда приветствуются. Протестированный и с хорошим покрытием код значительно упрощает задачу проверки
вашего предложения. Сломанные модульные тесты, как описание проблемы, тоже приветствуются.
### 5. Обновите CHANGELOG
Отредактируйте файл CHANGELOG, включив ваши изменения. Для этого нужно вставить строки в начало файла под заголовком
"Work in progress", строки с описанием изменений должны выглядеть похожими на следующие:
```
Bug #999: a description of the bug fix (Your Name)
Enh #999: a description of the enhancement (Your Name)
```
`#999` это номер issue описывающей `Bug` или `Enh`.
Лог изменений должен быть сгруппирован по типу (`Bug`,`Enh`) и отсортирован по номеру issue.
Для очень маленьких исправлений, например опечаток и изменений документации, нет необходимости обновлять CHANGELOG.
### 6. Фиксация ваших изменений
Добавляем файлы/изменения которые вы хотите зафиксировать в [staging area](http://gitref.org/basic/#add)
```
git add path/to/my/file.php
```
Вы можете использовать опцию `-p` для того, чтоб выбрать, какие изменения вы хотите добавить в коммит.
Фиксируйте ваши изменения с описательным сообщением. Убедитесь что в сообщение есть номер `#XXX`, так github
автоматически свяжет ваш коммит с тикетом:
```
git commit -m "A brief description of this change which fixes #999 goes here"
```
### 7. Получение последнего кода из апстрима Yii в вашу ветку
```
git pull upstream master
```
Это гарантирует, что вы используете самый последний код в вашей ветке перед отправкой запроса на изменения. Если есть
какие-либо конфликты слияния, вы должны исправить их и зафиксировать изменения ещё раз. Это гарантирует, что команда Yii
сможет слить ваши изменения одним кликом.
### 8. Разрешив зависимости, оптравляем код на github