Skip to content

Commit

Permalink
add source code
Browse files Browse the repository at this point in the history
  • Loading branch information
cetver committed Jul 19, 2017
1 parent d47673b commit 7a6bb08
Show file tree
Hide file tree
Showing 25 changed files with 6,839 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .coveralls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
service_name: travis-pro
repo_token: ClIMW8GwAaFvgTl1yhWfOWxmnuIFbf34K
coverage_clover: tests/_output/coverage.xml
json_path: tests/_output/coveralls-upload.json
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Ignore all test for archive
/.github export-ignore
/.gitattributes export-ignore
/.coveralls.yml export-ignore
/.travis.yml export-ignore
/codeception.yml export-ignore
/tests export-ignore
26 changes: 26 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
dist: trusty

sudo: false

language: php

php:
- '5.4'
- '5.5'
- '5.6'
- '7.0'
- '7.1'

install:
- composer config --global github-oauth.github.com b7244dc99194b22416d5ce46d511ce04049b23de
- composer global require "fxp/composer-asset-plugin:~1.3.1"
- composer update --prefer-dist --no-interaction

script:
- vendor/bin/codecept run unit --coverage-xml
- vendor/bin/coveralls

# cache vendor dirs
cache:
directories:
- $HOME/.composer/cache
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
Language Url Manager
=====


[![Build Status](https://travis-ci.org/cetver/yii2-language-url-manager.svg?branch=master)](https://travis-ci.org/cetver/yii2-language-url-manager)
[![Coverage Status](https://coveralls.io/repos/github/cetver/yii2-language-url-manager/badge.svg?branch=master)](https://coveralls.io/github/cetver/yii2-language-url-manager?branch=master)

Parses and creates URLs containing languages

Installation
------------

The preferred way to install this extension is through [composer](http://getcomposer.org/download/).

Either run

```
composer require --prefer-dist cetver/yii2-language-url-manager
```

or add

```
"cetver/yii2-language-url-manager": "^1.0"
```

to the require section of your `composer.json` file.


Usage
-----

Update the web-application configuration file

```php
return [
'components' => [
'urlManager' => [
'class' => 'cetver\LanguageUrlManager\UrlManager',
'enablePrettyUrl' => true,
'showScriptName' => false,
/*
* The list of available languages.
*/
'languages' => ['en', 'ru'],
/*
or
'languages' => function () {
return \app\models\Language::find()->select('code')->column();
},
*/
/*
* - true: processes the URL like "en.example.com"
* - false: processes the URL like "example.com/en"
* NOTE: If this property set to true, the domain containing a language, must be the first on the left side,
* for example:
* - en.it.example.com - is valid
* - it.en.example.com - is invalid
*/
'existsLanguageSubdomain' => false,
/*
* The regular expression patterns list, applied to path info, if there are matches, the request,
* containing a language, will not be processed.
* For performance reasons, the blacklist does not applied for URL creation (Take a look at an example).
* @see \yii\web\Request::getPathInfo()
* An example:
* ```php
* [
* '/^api.*$/'
* ]
* ```
* - Requesting the blacklisted URL
* - $existsLanguageSubdomain = true
* - en.example.com/api (404 Not Found)
* - en.example.com/api/create (404 Not Found)
* - $existsLanguageSubdomain = false
* - example.com/en/api (404 Not Found)
* - example.com/en/api/create (404 Not Found)
* - Creating the blacklisted URL
* - echo \yii\helpers\Html::a('API', ['api/index', Yii::$app->urlManager->queryParam => null]);
*/
'blacklist' => [],
/*
* The query parameter name that contains a language.
*/
'queryParam' => 'language'
],
]
];
```
Tests
-----
Run the following commands
```
composer create-project --prefer-source cetver/yii2-language-url-manager
cd yii2-language-url-manager
vendor/bin/codecept run unit
```
226 changes: 226 additions & 0 deletions UrlManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
<?php

namespace cetver\LanguageUrlManager;

use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
use yii\web\Request;

/**
* Class UrlManager parses and creates URLs containing languages.
*
* @package cetver\LanguageUrlManager
*/
class UrlManager extends \yii\web\UrlManager
{
/**
* The host separator.
*/
const SEPARATOR_HOST = '.';
/**
* The request path separator.
*/
const SEPARATOR_PATH = '/';
/**
* The "www" domain.
*/
const DOMAIN_WWW = 'www';
/**
* @var array|callable the list of available languages.
*/
public $languages = [];
/**
* @var bool
* - true: processes the URL like "en.example.com"
* - false: processes the URL like "example.com/en"
* NOTE: If this property set to true, the domain containing a language, must be the first on the left side,
* for example:
* - en.it.example.com - is valid
* - it.en.example.com - is invalid
*/
public $existsLanguageSubdomain = false;
/**
* @var array the regular expression patterns list, applied to path info, if there are matches, the request,
* containing a language, will not be processed.
* For performance reasons, the blacklist does not applied for URL creation (Take a look at an example).
* @see \yii\web\Request::getPathInfo()
* An example:
* ```php
* [
* '/^api.*$/'
* ]
* ```
* - Requesting the blacklisted URL
* - $existsLanguageSubdomain = true
* - en.example.com/api (404 Not Found)
* - en.example.com/api/create (404 Not Found)
* - $existsLanguageSubdomain = false
* - example.com/en/api (404 Not Found)
* - example.com/en/api/create (404 Not Found)
* - Creating the blacklisted URL
* - echo \yii\helpers\Html::a('API', ['api/index', Yii::$app->urlManager->queryParam => null]);
*/
public $blacklist = [];
/**
* @var string the query parameter name that contains a language.
* @see \yii\web\Request::getQueryParams()
*/
public $queryParam = 'language';

/**
* @inheritdoc
*/
public function init()
{
if (!$this->enablePrettyUrl) {
throw new InvalidConfigException(
'The "enablePrettyUrl" property must be set to "true"'
);
}
if (is_callable($this->languages)) {
$this->languages = call_user_func($this->languages);
}
if (!is_array($this->languages)) {
throw new InvalidConfigException(
'The "languages" property must be an array or callable function that returns an array'
);
}
parent::init();
}

/**
* @inheritdoc
*/
public function parseRequest($request)
{
$pathInfo = $request->getPathInfo();
if (!$this->existsLanguageSubdomain) {
$language = explode(self::SEPARATOR_PATH, $pathInfo)[0];
if (in_array($language, $this->languages)) {
$pathInfo = ltrim($pathInfo, $language);
if (!$this->isBlacklisted($pathInfo)) {
$request->setPathInfo($pathInfo);
$this->setQueryParam($request, $language);
}
}

return parent::parseRequest($request);
} else {
$hostChunks = $this->getHostChunks($request);
$language = ArrayHelper::getValue($hostChunks, 0);
if (!in_array($language, $this->languages)) {
return parent::parseRequest($request);
} else {
if ($this->isBlacklisted($pathInfo)) {
return false;
} else {
$this->setQueryParam($request, $language);

return parent::parseRequest($request);
}
}
}
}

/**
* @inheritdoc
*/
public function createUrl($params)
{
$request = Yii::$app->getRequest();
if (!$this->existsLanguageSubdomain) {
$language = ArrayHelper::remove(
$params,
$this->queryParam,
$request->getQueryParam($this->queryParam)
);
$url = parent::createUrl($params);
if (!in_array($language, $this->languages)) {
return $url;
} else {
$baseUrl = $this->getBaseUrl();
$url = implode(self::SEPARATOR_PATH, [
$baseUrl,
$language,
ltrim($url, $baseUrl),
]);
$pattern = sprintf('#%s{2,}#', self::SEPARATOR_PATH);
$url = preg_replace($pattern, self::SEPARATOR_PATH, $url);

return $url;
}
} else {
$language = ArrayHelper::remove($params, $this->queryParam);
if (in_array($language, $this->languages)) {
$hostChunks = $this->getHostChunks($request);
if ($hostChunks[0] === self::DOMAIN_WWW) {
array_shift($hostChunks);
}
if (in_array($hostChunks[0], $this->languages)) {
$hostChunks[0] = $language;
} else {
array_unshift($hostChunks, $language);
}
$protocol = ($request->getIsSecureConnection()) ? 'https' : 'http';
$protocol .= '://';
$host = implode(self::SEPARATOR_HOST, $hostChunks);
$url = parent::createUrl($params);

return $protocol . $host . $url;
}

return parent::createUrl($params);
}
}

/**
* Returns the "Host" header value splitted by the separator.
*
* @see \cetver\LanguageUrlManager\UrlManager::SEPARATOR_HOST
*
* @param Request $request the Request component instance.
*
* @return array
*/
protected function getHostChunks(Request $request)
{
$host = parse_url($request->getHostInfo(), PHP_URL_HOST);

return explode(self::SEPARATOR_HOST, $host);
}

/**
* Sets the query parameter that contains a language.
*
* @param Request $request the Request component instance.
* @param string $value a language value.
*/
protected function setQueryParam(Request $request, $value)
{
$queryParams = $request->getQueryParams();
$queryParams[$this->queryParam] = $value;
$request->setQueryParams($queryParams);
}

/**
* Returns whether the path info is blacklisted.
*
* @see $blacklist
*
* @param string $pathInfo the path info of the currently requested URL.
*
* @return bool
*/
protected function isBlacklisted($pathInfo)
{
$pathInfo = ltrim($pathInfo, self::SEPARATOR_PATH);
foreach ($this->blacklist as $pattern) {
if (preg_match($pattern, $pathInfo)) {
return true;
}
}

return false;
}
}
17 changes: 17 additions & 0 deletions codeception.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace: cetver\LanguageUrlManager\tests
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed
settings:
bootstrap: _bootstrap.php
coverage:
enabled: true
include:
- UrlManager.php
Loading

0 comments on commit 7a6bb08

Please sign in to comment.