Browse Source
Conflicts: docs/guide/runtime-logging.md framework/base/Security.php framework/di/Container.php framework/filters/auth/HttpBearerAuth.php framework/grid/DataColumn.php tests/framework/filters/RateLimiterTest.php tests/framework/helpers/HtmlTest.php tests/framework/rest/UrlRuleTest.phptags/3.0.0-alpha1
Klimov Paul
7 years ago
130 changed files with 2764 additions and 366 deletions
@ -0,0 +1,206 @@
|
||||
# Использование Yii в качестве микро-framework'а |
||||
|
||||
Yii можно легко использовать без функций включенных в базовый и расширенный шаблоны приложений. Другими словами Yii уже является микро-каркасом. Не требуется иметь структуру каталогов предоставляемую этими шаблонами при работе с Yii. |
||||
|
||||
Это особенно удобно, когда Вам не нужен весь пред-установленный шаблонный код, такой как `Assets` или `Views`. Одним из таких случаев является создание JSON API. В следующих разделах будет показано, как это сделать. |
||||
|
||||
## Установка Yii |
||||
|
||||
Создайте каталог для файлов проекта и смените рабочий каталог на этот путь. В примерах используются команды Unix, но аналогичные команды существуют и в Windows. |
||||
|
||||
```bash |
||||
mkdir micro-app |
||||
cd micro-app |
||||
``` |
||||
|
||||
> Note: Для продолжения требуется немного знаний о Composer. Если Вы еще не знаете, как использовать Composer, пожалуйста, найдите время, чтобы прочитать [Руководство Composer](https://getcomposer.org/doc/00-intro.md). |
||||
|
||||
Создайте файл `composer.json` в каталоге `micro-app` с помощью Вашего любимого редактора и добавьте следующее: |
||||
|
||||
```json |
||||
{ |
||||
"require": { |
||||
"yiisoft/yii2": "~2.0.0" |
||||
}, |
||||
"repositories": [ |
||||
{ |
||||
"type": "composer", |
||||
"url": "https://asset-packagist.org" |
||||
} |
||||
] |
||||
} |
||||
``` |
||||
|
||||
Сохраните файл и запустите команду `comper install`. Это установит framework со всеми его зависимостями. |
||||
|
||||
## Создание структуры проекта |
||||
|
||||
После того как Вы установили фреймворк, пришло время создать [входную точку](structure-entry-scripts.md) приложения. Точка входа - это самый первый файл, который будет выполнен при попытке открыть приложение. По соображениям безопасности рекомендуется поместить файл точки входа в отдельный каталог и сделать каталог корнем веб директории. |
||||
|
||||
Создайте каталог `web` и поместите в него файл `index.php` со следующим содержимым: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
// закомментируйте следующие две строки при использовании в рабочем режиме |
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
defined('YII_ENV') or define('YII_ENV', 'dev'); |
||||
|
||||
require(__DIR__ . '/../vendor/autoload.php'); |
||||
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); |
||||
|
||||
$config = require __DIR__ . '/../config.php'; |
||||
(new yii\web\Application($config))->run(); |
||||
``` |
||||
|
||||
Также создайте файл с именем `config.php`, который будет содержать всю конфигурацию приложения: |
||||
|
||||
```php |
||||
<?php |
||||
return [ |
||||
'id' => 'micro-app', |
||||
// basePath (базовый путь) приложения будет каталог `micro-app` |
||||
'basePath' => __DIR__, |
||||
// это пространство имен где приложение будет искать все контроллеры |
||||
'controllerNamespace' => 'micro\controllers', |
||||
// установим псевдоним '@micro', чтобы включить автозагрузку классов из пространства имен 'micro' |
||||
'aliases' => [ |
||||
'@micro' => __DIR__, |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
> Info: Несмотря на то, что конфигурация приложения может находиться в файле `index.php` рекомендуется |
||||
> содержать её в отдельном файле. Таким образом её можно также использовать и для консольного приложения, как показано ниже. |
||||
|
||||
Теперь Ваш проект готов к наполнению кодом. Вам решать какую структуру каталогов проекта Вы выберите, пока Вы сможете видеть пространства имен. |
||||
|
||||
## Создание первого контроллера |
||||
|
||||
Создайте каталог `controllers` и добавьте туда файл `SiteController.php` который является контроллером по умолчанию, он будет обрабатывать запрос без пути. |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
namespace micro\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionIndex() |
||||
{ |
||||
return 'Hello World!'; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Если Вы хотите использовать другое имя для этого контроллера, Вы можете изменить его настроив [[yii\base\Application::$defaultRoute]]. |
||||
Например для `DefaultController` будет соответственно `'defaultRoute' => 'default/index'`. |
||||
|
||||
На данный момент структура проекта должна выглядеть так: |
||||
|
||||
``` |
||||
micro-app/ |
||||
├── composer.json |
||||
├── web/ |
||||
└── index.php |
||||
└── controllers/ |
||||
└── SiteController.php |
||||
``` |
||||
|
||||
Если Вы еще не настроили веб-сервер, Вы можете взглянуть на [примеры конфигурационных файлов веб-серверов](start-installation.md#Настройка-веб-сервера-). |
||||
Другой возможностью является использование команды `yii serve` которая будет использовать встроенный веб-сервер PHP. Вы можете запустить её из каталога `micro-app/` через: |
||||
|
||||
vendor/bin/yii serve --docroot=./web |
||||
|
||||
При открытии URL приложения в браузере, он теперь должен печатать "Hello World!" который был возвращен из `SiteController::actionIndex()`. |
||||
|
||||
> Info: В нашем примере мы изменили пространство имен по умолчанию приложения с `app` на` micro`, чтобы продемонстрировать |
||||
> что Вы не привязаны к этому имени (в случае, если Вы считали, что это так), а затем скорректировали |
||||
> [[yii\base\Application::$controllerNamespace|controllers namespace]] и установили правильный псевдоним. |
||||
|
||||
## Создание REST API |
||||
|
||||
Чтобы продемонстрировать использование нашей "микроархитектуры" мы создадим простой REST API для сообщений. |
||||
|
||||
Чтобы этот API обслуживал некоторые данные, нам нужна база данных. Добавим конфигурацию подключения базы данных |
||||
к конфигурации приложения: |
||||
|
||||
```php |
||||
'components' => [ |
||||
'db' => [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'sqlite:@micro/database.sqlite', |
||||
], |
||||
], |
||||
``` |
||||
|
||||
> Info: Для простоты мы используем базу данных sqlite. Дополнительную информацию см. в [Руководство по базам данных](db-dao.md). |
||||
|
||||
Затем мы создаем [миграции базы данных](db-migrations.md) для создания таблицы сообщений. |
||||
Убедитесь, что у Вас есть отдельный файл конфигурации, как описано выше, нам это нужно для того, чтобы запустить консольные команды описанные ниже. |
||||
Запуск следующих команд создаст файл миграции базы данных и применит миграцию к базе данных: |
||||
|
||||
vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields="title:string,body:text" |
||||
vendor/bin/yii migrate/up --appconfig=config.php |
||||
|
||||
Создайте каталог `models` и файл` Post.php` в этом каталоге. Это код модели: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
namespace micro\models; |
||||
|
||||
use yii\db\ActiveRecord; |
||||
|
||||
class Post extends ActiveRecord |
||||
{ |
||||
public static function tableName() |
||||
{ |
||||
return '{{posts}}'; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Info: Созданная модель представляет собой класс ActiveRecord, который представляет данные из таблицы `posts`. |
||||
> Для получения дополнительной информации обратитесь к [active record руководству](db-active-record.md). |
||||
|
||||
Чтобы обслуживать сообщения в нашем API, добавьте `PostController` в` controllers`: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
namespace micro\controllers; |
||||
|
||||
use yii\rest\ActiveController; |
||||
|
||||
class PostController extends ActiveController |
||||
{ |
||||
public $modelClass = 'micro\models\Post'; |
||||
|
||||
public function behaviors() |
||||
{ |
||||
// удаляем rateLimiter, требуется для аутентификации пользователя |
||||
$behaviors = parent::behaviors(); |
||||
unset($behaviors['rateLimiter']); |
||||
return $behaviors; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
На этом этапе наш API предоставляет следующие URL-адреса: |
||||
|
||||
- `/index.php?r=post` - список всех сообщений |
||||
- `/index.php?r=post/view&id=1` - просмотр сообщения с ID 1 |
||||
- `/index.php?r=post/create` - создание сообщения |
||||
- `/index.php?r=post/update&id=1` - обновление сообщения with ID 1 |
||||
- `/index.php?r=post/delete&id=1` - удаление сообщения with ID 1 |
||||
|
||||
Начиная с этого момента Вы можете посмотреть следующие руководства для дальнейшего развития своего приложения: |
||||
|
||||
- API в настоящий момент понимает только данные urlencoded как входные данные, чтобы сделать его настоящим JSON API, Вам |
||||
необходимо настроить [[yii\web\JsonParser]]. |
||||
- Чтобы сделать URL более дружественным, вам необходимо настроить маршрутизацию. |
||||
См. [Руководство по маршрутизации REST](rest-routing.md) о том, как это сделать. |
||||
- Дополнительную информацию см. в разделе [Взгляд в будущее](start-looking-ahead.md). |
@ -0,0 +1,22 @@
|
||||
# What do you need to know |
||||
|
||||
Yii learning curve is not as steep as other PHP frameworks but still it requires some beforehand knowledge. |
||||
|
||||
## PHP |
||||
|
||||
Yii is a PHP framework so make sure you [read and understand language reference](http://php.net/manual/en/langref.php). |
||||
|
||||
## Object oriented programming |
||||
|
||||
Basic understanding of object oriented programming is required. If you're not familiar with it, check one of the many |
||||
tutorials available such as [the one from tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762). |
||||
|
||||
Note that the more complicated your application is the more advanced OOP concepts your should learn in order to successfully |
||||
manage that complexity. |
||||
|
||||
## Command line and composer |
||||
|
||||
Yii extensively uses de-facto standard PHP package manager, [Composer](https://getcomposer.org/) so make sure you read |
||||
and understand its guide. If you are not familiar with using command line it is time to start trying. Once you'll |
||||
learn the basics you'll never want to work without it. |
||||
|
@ -0,0 +1,116 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\db\sqlite; |
||||
|
||||
use yii\db\SqlToken; |
||||
use yii\helpers\StringHelper; |
||||
|
||||
/** |
||||
* Command represents an SQLite's SQL statement to be executed against a database. |
||||
* |
||||
* {@inheritdoc} |
||||
* |
||||
* @author Sergey Makinen <sergey@makinen.ru> |
||||
* @since 2.0.14 |
||||
*/ |
||||
class Command extends \yii\db\Command |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function execute() |
||||
{ |
||||
$sql = $this->getSql(); |
||||
$params = $this->params; |
||||
$statements = $this->splitStatements($sql, $params); |
||||
if ($statements === false) { |
||||
return parent::execute(); |
||||
} |
||||
|
||||
$result = null; |
||||
foreach ($statements as $statement) { |
||||
list($statementSql, $statementParams) = $statement; |
||||
$this->setSql($statementSql)->bindValues($statementParams); |
||||
$result = parent::execute(); |
||||
} |
||||
$this->setSql($sql)->bindValues($params); |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
protected function queryInternal($method, $fetchMode = null) |
||||
{ |
||||
$sql = $this->getSql(); |
||||
$params = $this->params; |
||||
$statements = $this->splitStatements($sql, $params); |
||||
if ($statements === false) { |
||||
return parent::queryInternal($method, $fetchMode); |
||||
} |
||||
|
||||
list($lastStatementSql, $lastStatementParams) = array_pop($statements); |
||||
foreach ($statements as $statement) { |
||||
list($statementSql, $statementParams) = $statement; |
||||
$this->setSql($statementSql)->bindValues($statementParams); |
||||
parent::execute(); |
||||
} |
||||
$this->setSql($lastStatementSql)->bindValues($lastStatementParams); |
||||
$result = parent::queryInternal($method, $fetchMode); |
||||
$this->setSql($sql)->bindValues($params); |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Splits the specified SQL code into individual SQL statements and returns them |
||||
* or `false` if there's a single statement. |
||||
* @param string $sql |
||||
* @param array $params |
||||
* @return string[]|false |
||||
*/ |
||||
private function splitStatements($sql, $params) |
||||
{ |
||||
$semicolonIndex = strpos($sql, ';'); |
||||
if ($semicolonIndex === false || $semicolonIndex === StringHelper::byteLength($sql) - 1) { |
||||
return false; |
||||
} |
||||
|
||||
$tokenizer = new SqlTokenizer($sql); |
||||
$codeToken = $tokenizer->tokenize(); |
||||
if (count($codeToken->getChildren()) === 1) { |
||||
return false; |
||||
} |
||||
|
||||
$statements = []; |
||||
foreach ($codeToken->getChildren() as $statement) { |
||||
$statements[] = [$statement->getSql(), $this->extractUsedParams($statement, $params)]; |
||||
} |
||||
return $statements; |
||||
} |
||||
|
||||
/** |
||||
* Returns named bindings used in the specified statement token. |
||||
* @param SqlToken $statement |
||||
* @param array $params |
||||
* @return array |
||||
*/ |
||||
private function extractUsedParams(SqlToken $statement, $params) |
||||
{ |
||||
preg_match_all('/(?P<placeholder>[:][a-zA-Z0-9_]+)/', $statement->getSql(), $matches, PREG_SET_ORDER); |
||||
$result = []; |
||||
foreach ($matches as $match) { |
||||
$phName = ltrim($match['placeholder'], ':'); |
||||
if (isset($params[$phName])) { |
||||
$result[$phName] = $params[$phName]; |
||||
} elseif (isset($params[':' . $phName])) { |
||||
$result[':' . $phName] = $params[':' . $phName]; |
||||
} |
||||
} |
||||
return $result; |
||||
} |
||||
} |
@ -0,0 +1,67 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\filters\auth; |
||||
|
||||
/** |
||||
* HttpHeaderAuth is an action filter that supports HTTP authentication through HTTP Headers. |
||||
* |
||||
* You may use HttpHeaderAuth by attaching it as a behavior to a controller or module, like the following: |
||||
* |
||||
* ```php |
||||
* public function behaviors() |
||||
* { |
||||
* return [ |
||||
* 'basicAuth' => [ |
||||
* 'class' => \yii\filters\auth\HttpHeaderAuth::className(), |
||||
* ], |
||||
* ]; |
||||
* } |
||||
* ``` |
||||
* |
||||
* The default implementation of HttpHeaderAuth uses the [[\yii\web\User::loginByAccessToken()|loginByAccessToken()]] |
||||
* method of the `user` application component and passes the value of the `X-Api-Key` header. This implementation is used |
||||
* for authenticating API clients. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @author Benoît Boure <benoit.boure@gmail.com> |
||||
* @since 2.0.14 |
||||
*/ |
||||
class HttpHeaderAuth extends AuthMethod |
||||
{ |
||||
/** |
||||
* @var string the HTTP header name |
||||
*/ |
||||
public $header = 'X-Api-Key'; |
||||
|
||||
/** |
||||
* @var string a pattern to use to extract the HTTP authentication value |
||||
*/ |
||||
public $pattern; |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function authenticate($user, $request, $response) |
||||
{ |
||||
$authHeader = $request->getHeaders()->get($this->header); |
||||
|
||||
if ($authHeader !== null) { |
||||
if ($this->pattern !== null && preg_match($this->pattern, $authHeader, $matches)) { |
||||
$authHeader = $matches[1]; |
||||
} |
||||
$identity = $user->loginByAccessToken($authHeader, get_class($this)); |
||||
if ($identity === null) { |
||||
$this->handleFailure($response); |
||||
} |
||||
|
||||
return $identity; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\web; |
||||
|
||||
use yii\base\Exception; |
||||
|
||||
/** |
||||
* HeadersAlreadySentException represents an exception caused by |
||||
* any headers that were already sent before web response was sent. |
||||
* |
||||
* @author Dmitry Dorogin <dmirogin@ya.ru> |
||||
* @since 2.0.14 |
||||
*/ |
||||
class HeadersAlreadySentException extends Exception |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function __construct($file, $line) |
||||
{ |
||||
$message = YII_DEBUG ? "Headers already sent in {$file} on line {$line}." : 'Headers already sent.'; |
||||
parent::__construct($message); |
||||
} |
||||
} |
@ -1,10 +1,8 @@
|
||||
# docker-compose test environment |
||||
|
||||
# Choose a flavour |
||||
#COMPOSE_FILE=docker-compose.yml:docker-compose.caching.yml |
||||
#COMPOSE_FILE=docker-compose.yml:docker-compose.mysql.yml |
||||
#COMPOSE_FILE=docker-compose.yml:docker-compose.pgsql.yml |
||||
# Configuration files |
||||
COMPOSE_FILE=docker-compose.yml:docker-compose.mysql.yml:docker-compose.pgsql.yml:docker-compose.caching.yml |
||||
|
||||
# Choose a version |
||||
# Image versions |
||||
DOCKER_MYSQL_IMAGE=percona:5.7 |
||||
DOCKER_POSTGRES_IMAGE=postgres |
||||
|
@ -0,0 +1,54 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yiiunit\framework; |
||||
|
||||
use yiiunit\TestCase; |
||||
|
||||
/** |
||||
* ChangeLogTest. |
||||
* @group base |
||||
*/ |
||||
class ChangeLogTest extends TestCase |
||||
{ |
||||
public function changeProvider() |
||||
{ |
||||
|
||||
$lines = explode("\n", file_get_contents(__DIR__ . '/../../framework/CHANGELOG.md')); |
||||
|
||||
// Don't check last 1500 lines, they are old and often don't obey the standard. |
||||
$lastIndex = count($lines) - 1500; |
||||
$result = []; |
||||
foreach($lines as $i => $line) { |
||||
if (strncmp('- ', $line, 2) === 0) { |
||||
$result[] = [$line]; |
||||
} |
||||
|
||||
if ($i > $lastIndex) { |
||||
break; |
||||
} |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider changeProvider |
||||
*/ |
||||
public function testContributorLine($line) |
||||
{ |
||||
/** |
||||
* Each change line is tested for: |
||||
* - Starts with "- " |
||||
* - Has a type: Bug, Enh, Chg, New |
||||
* - Has a number formatted like #12345 |
||||
* - Description starts after ": " |
||||
* - Description ends without a "." |
||||
* - Line ends with contributor name between "(" and ")". |
||||
*/ |
||||
$this->assertRegExp('/- (Bug|Enh|Chg|New)( #\d+(, #\d+)*)?: .*[^.] \(.*\)$/', $line); |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return new class() extends \yii\base\Model |
||||
{ |
||||
|
||||
}; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue