Skip to content

Commit 31d4e0d

Browse files
committed
first commit
1 parent 97443ff commit 31d4e0d

3 files changed

Lines changed: 193 additions & 0 deletions

File tree

composer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "fusic/Maintenance",
3+
"description": "Maintenance plugin for CakePHP",
4+
"type": "cakephp-plugin",
5+
"require": {
6+
"php": ">=5.5.9",
7+
"cakephp/cakephp": ">=3.3.2 <4.0.0"
8+
},
9+
"require-dev": {
10+
"phpunit/phpunit": "*"
11+
},
12+
"autoload": {
13+
"psr-4": {
14+
"Maintenance\\": "src"
15+
}
16+
},
17+
"autoload-dev": {
18+
"psr-4": {
19+
"Maintenance\\Test\\": "tests",
20+
"Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
21+
}
22+
}
23+
}

phpunit.xml.dist

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit
3+
colors="true"
4+
processIsolation="false"
5+
stopOnFailure="false"
6+
syntaxCheck="false"
7+
bootstrap="./tests/bootstrap.php"
8+
>
9+
<php>
10+
<ini name="memory_limit" value="-1"/>
11+
<ini name="apc.enable_cli" value="1"/>
12+
</php>
13+
14+
<!-- Add any additional test suites you want to run here -->
15+
<testsuites>
16+
<testsuite name="Maintenance Test Suite">
17+
<directory>./tests/TestCase</directory>
18+
</testsuite>
19+
</testsuites>
20+
21+
<!-- Setup a listener for fixtures -->
22+
<listeners>
23+
<listener
24+
class="\Cake\TestSuite\Fixture\FixtureInjector"
25+
file="./vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php">
26+
<arguments>
27+
<object class="\Cake\TestSuite\Fixture\FixtureManager" />
28+
</arguments>
29+
</listener>
30+
</listeners>
31+
32+
<!-- Prevent coverage reports from looking in tests and vendors -->
33+
<filter>
34+
<blacklist>
35+
<directory suffix=".php">./vendor/</directory>
36+
<directory suffix=".ctp">./vendor/</directory>
37+
38+
<directory suffix=".php">./tests/</directory>
39+
<directory suffix=".ctp">./tests/</directory>
40+
</blacklist>
41+
</filter>
42+
43+
</phpunit>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
namespace Maintenance\Middleware;
3+
4+
use Cake\Core\InstanceConfigTrait;
5+
use Cake\Utility\Inflector;
6+
use Cake\View\ViewBuilder;
7+
use Cake\Network\Request;
8+
9+
class MaintenanceMiddleware
10+
{
11+
use InstanceConfigTrait;
12+
13+
/**
14+
* Default config.
15+
*
16+
* @var array
17+
*/
18+
protected $_defaultConfig = [
19+
'allowIp' => [],
20+
21+
'className' => 'Cake\View\View',
22+
'templatePath' => 'Error',
23+
24+
'statusFilePath' => TMP,
25+
'statusFileName' => 'maintenance',
26+
'statusCode' => 503,
27+
28+
'ctpFileName' => 'maintenance',
29+
'ctpExtension' => '.ctp',
30+
31+
'contentType' => 'text/html'
32+
];
33+
34+
public function __construct($config = [])
35+
{
36+
$this->config($config);
37+
}
38+
39+
public function __invoke($request, $response, $next)
40+
{
41+
$isActive = $this->isMaintenance($request);
42+
43+
if ($isActive === false) {
44+
$response = $next($request, $response);
45+
} else {
46+
$response = $this->execute($response);
47+
}
48+
49+
return $response;
50+
}
51+
52+
private function execute($response)
53+
{
54+
$cakeRequest = Request::createFromGlobals();
55+
$builder = new ViewBuilder();
56+
57+
$className = $this->config('className');
58+
$templateName = $this->config('ctpFileName');
59+
$templatePath = $this->config('templatePath');
60+
$ext = $this->config('ctpExtension');
61+
$contentType = $this->config('contentType');
62+
$statusCode = $this->config('statusCode');
63+
64+
$view = $builder
65+
->className($className)
66+
->templatePath(Inflector::camelize($templatePath))
67+
->layout(false)
68+
->build([], $cakeRequest);
69+
$view->_ext = $ext;
70+
$bodyString = $view->render($templateName);
71+
72+
$body = $response->withHeader('Content-Type', $contentType)
73+
->withStatus($statusCode)
74+
->getBody();
75+
$body->write($bodyString);
76+
77+
return $response;
78+
}
79+
80+
private function isMaintenance($request)
81+
{
82+
$fullPath = $this->config('statusFilePath') . $this->config('statusFileName');
83+
$ret = file_exists($fullPath);
84+
if ($ret === false) {
85+
return false;
86+
}
87+
88+
$ret = $this->isAllowIp($request);
89+
if ($ret === true) {
90+
return false;
91+
}
92+
93+
return true;
94+
}
95+
96+
private function isAllowIp($request)
97+
{
98+
$params = $request->getServerParams();
99+
$myIpAddress = $params['REMOTE_ADDR'];
100+
101+
$ipAddressList = $this->config('allowIp');
102+
if (empty($ipAddressList)) {
103+
return false;
104+
}
105+
106+
foreach ($ipAddressList as $allowIP) {
107+
// サブネットマスクが指定されていない場合 /32 を追加
108+
if (strpos($allowIP, '/') == 0) {
109+
$allowIP .= '/32';
110+
}
111+
// IPアドレスの書式チェック
112+
if (!preg_match('/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\/([1-9]|1[0-9]|2[0-9]|3[0-2])$/', $allowIP)) {
113+
// 書式が不正
114+
continue;
115+
}
116+
list($ip, $maskBit) = explode("/", $allowIP);
117+
$ipLong = ip2long($ip) >> (32 - $maskBit);
118+
119+
$selfIpLong = ip2long($myIpAddress) >> (32 - $maskBit);
120+
if ($selfIpLong === $ipLong) {
121+
return true;
122+
}
123+
}
124+
125+
return false;
126+
}
127+
}

0 commit comments

Comments
 (0)