Skip to content

Commit 7dbcac5

Browse files
Merge pull request #1 from demvsystems/BIP-1214
Bip 1214
2 parents 92d96e2 + f831d86 commit 7dbcac5

File tree

7 files changed

+372
-1
lines changed

7 files changed

+372
-1
lines changed

.github/workflows/backend.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: backend
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- master
8+
pull_request:
9+
types:
10+
- opened
11+
- synchronize
12+
- reopened
13+
- ready_for_review
14+
15+
env:
16+
PHP_EXTENSIONS: zip
17+
PHP_TOOLS: composer:v1
18+
PHP_EXTENSIONS_CACHE_KEY: php-extensions-cache-v1
19+
PHP_VERSION: '7.1'
20+
21+
jobs:
22+
lint-and-test:
23+
runs-on: ubuntu-18.04
24+
if: github.event.pull_request.draft == false
25+
env:
26+
APP_ENV: test
27+
steps:
28+
- name: Cancel Previous Runs
29+
uses: styfle/cancel-workflow-action@0.6.0
30+
with:
31+
ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
33+
- name: Checkout
34+
uses: actions/checkout@v2
35+
36+
- name: Setup php extensions cache
37+
id: extcache
38+
uses: shivammathur/cache-extensions@v1
39+
with:
40+
php-version: ${{ env.PHP_VERSION }}
41+
extensions: ${{ env.PHP_EXTENSIONS }}
42+
key: ${{ env.PHP_EXTENSIONS_CACHE_KEY }}
43+
44+
- name: Cache php extensions
45+
uses: actions/cache@v2
46+
with:
47+
path: ${{ steps.extcache.outputs.dir }}
48+
key: ${{ steps.extcache.outputs.key }}
49+
restore-keys: ${{ steps.extcache.outputs.key }}
50+
51+
- name: Setup PHP
52+
uses: shivammathur/setup-php@v2
53+
with:
54+
php-version: ${{ env.PHP_VERSION }}
55+
tools: ${{ env.PHP_TOOLS }}
56+
ini-values: memory_limit=512M, short_open_tag=On
57+
coverage: none
58+
extensions: ${{ env.PHP_EXTENSIONS }}
59+
env:
60+
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61+
62+
- name: Display PHP information
63+
run: |
64+
php -v
65+
php -m
66+
composer --version
67+
68+
- name: Get composer cache directory
69+
id: composer-cache
70+
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
71+
72+
- name: Cache dependencies
73+
uses: actions/cache@v2
74+
with:
75+
path: ${{ steps.composer-cache.outputs.dir }}
76+
key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}-composer
77+
restore-keys: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}-composer
78+
79+
- name: Install dependencies
80+
run: composer install --no-interaction --no-suggest --no-scripts --prefer-dist --ansi
81+
82+
- name: Setup problem matchers for PHPUnit
83+
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
84+
85+
- name: Run Unit tests
86+
run: composer test --ansi

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ kill:
1818
docker-compose down --volumes --remove-orphans
1919

2020
test:
21-
docker-compose exec $(DOCKER_PHP_SERVICE) composer test
21+
docker-compose exec php-fpm composer test

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,10 @@
2525
"Demv\\Tmpdir\\": "src/",
2626
"Demv\\Tmpdir\\Test\\": "tests/"
2727
}
28+
},
29+
"scripts": {
30+
"test": [
31+
"phpunit --testsuite all"
32+
]
2833
}
2934
}

phpunit.xml.dist

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit colors="true" bootstrap="tests/_bootstrap.php">
4+
<testsuites>
5+
<testsuite name="all">
6+
<directory>tests</directory>
7+
</testsuite>
8+
</testsuites>
9+
</phpunit>

src/TmpDirRegistry.php

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
namespace Demv\Tmpdir;
4+
5+
use InvalidArgumentException;
6+
use RecursiveDirectoryIterator;
7+
use RecursiveIteratorIterator;
8+
use RuntimeException;
9+
10+
/**
11+
* Class TmpDirRegistry
12+
* @package Demv\Tmpdir
13+
*/
14+
final class TmpDirRegistry
15+
{
16+
/**
17+
* @var string[]
18+
*/
19+
private $dirs = [];
20+
/**
21+
* @var string[]
22+
*/
23+
private $files = [];
24+
/**
25+
* @var TmpDirRegistry
26+
*/
27+
private static $instance;
28+
29+
public function __destruct()
30+
{
31+
array_map(
32+
static function (string $filepath): void {
33+
self::deletefile($filepath);
34+
},
35+
$this->files
36+
);
37+
$this->files = [];
38+
array_map(
39+
static function (string $dir): void {
40+
self::deleteDir($dir);
41+
},
42+
$this->dirs
43+
);
44+
$this->dirs = [];
45+
}
46+
47+
/**
48+
* @return static
49+
*/
50+
public static function instance(): self
51+
{
52+
if (self::$instance === null) {
53+
self::$instance = new self();
54+
}
55+
56+
return self::$instance;
57+
}
58+
59+
/**
60+
* @param string $dirname
61+
*
62+
* @return string
63+
* @throws RuntimeException
64+
*/
65+
public function createDirInSystemTmp(string $dirname): string
66+
{
67+
$dirname = trim($dirname);
68+
if ($dirname === '') {
69+
throw new RuntimeException('Dirname must not be empty so we do not delete entire temp directory.');
70+
}
71+
72+
$dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . sprintf('%s_%s', $dirname, $this->getUniqid());
73+
if (!mkdir($dir, 0755, true) && !is_dir($dir)) {
74+
throw new RuntimeException(sprintf('Directory "%s" was not created', $dir));
75+
}
76+
$this->dirs[] = $dir;
77+
78+
return $dir;
79+
}
80+
81+
/**
82+
* @param string $dir
83+
* @param string $filename
84+
*
85+
* @return string
86+
*/
87+
public function createFileInSystemTmp(string $dir, string $filename): string
88+
{
89+
$filename = trim($filename);
90+
if ($filename === '') {
91+
throw new RuntimeException('Filename must not be empty so we do not delete entire temp directory.');
92+
}
93+
94+
$info = pathinfo($filename);
95+
if (!array_key_exists('extension', $info) || !array_key_exists('filename', $info)) {
96+
throw new RuntimeException('Can\'t read info from filename.');
97+
}
98+
99+
$filepath = $dir . DIRECTORY_SEPARATOR . sprintf('%s_%s.%s', $info['filename'], $this->getUniqid(), $info['extension']);
100+
$handle = fopen($filepath, 'wb+');
101+
fclose($handle);
102+
if (!file_exists($filepath)) {
103+
throw new RuntimeException(sprintf('File "%s" was not created', $filepath));
104+
}
105+
$this->files[] = $filepath;
106+
107+
return $filepath;
108+
}
109+
110+
/**
111+
* @param string $dirPath
112+
*
113+
* @throws InvalidArgumentException
114+
*/
115+
private static function deleteDir(string $dirPath): void
116+
{
117+
$dirPath = self::addSeperatorIfNecessary($dirPath);
118+
119+
if (!is_dir($dirPath)) {
120+
throw new InvalidArgumentException(sprintf('%s must be a directory', $dirPath));
121+
}
122+
123+
$files = self::getFiles($dirPath);
124+
125+
foreach ($files as $fileinfo) {
126+
$rm = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
127+
$rm($fileinfo->getRealPath());
128+
}
129+
130+
rmdir($dirPath);
131+
}
132+
133+
/**
134+
* @param string $filepath
135+
*/
136+
private static function deleteFile(string $filepath): void
137+
{
138+
if (!file_exists($filepath)) {
139+
throw new InvalidArgumentException(sprintf('%s must be a file', $filepath));
140+
}
141+
142+
unlink($filepath);
143+
}
144+
145+
/**
146+
* @param string $dirPath
147+
*
148+
* @return RecursiveIteratorIterator
149+
*/
150+
private static function getFiles(string $dirPath): RecursiveIteratorIterator
151+
{
152+
return new RecursiveIteratorIterator(
153+
new RecursiveDirectoryIterator($dirPath, RecursiveDirectoryIterator::SKIP_DOTS),
154+
RecursiveIteratorIterator::CHILD_FIRST
155+
);
156+
}
157+
158+
/**
159+
* @param string $dirPath
160+
*
161+
* @return string
162+
*/
163+
private static function addSeperatorIfNecessary(string $dirPath): string
164+
{
165+
return rtrim(trim($dirPath), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
166+
}
167+
168+
/**
169+
* @return string
170+
*/
171+
public function getUniqid(): string
172+
{
173+
return uniqid('', true);
174+
}
175+
}

tests/TmpDirRegistryTest.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Test\Demv\Tmpdir;
4+
5+
use Demv\Tmpdir\TmpDirRegistry;
6+
use PHPUnit\Framework\TestCase;
7+
8+
/**
9+
* Class TmpDirRegistryTest
10+
* @package Test\Demv\Tmpdir
11+
*/
12+
final class TmpDirRegistryTest extends TestCase
13+
{
14+
/**
15+
* @var TmpDirRegistry
16+
*/
17+
private $instance;
18+
19+
public function setUp(): void
20+
{
21+
parent::setUp();
22+
$this->instance = TmpDirRegistry::instance();
23+
}
24+
25+
public function tearDown(): void
26+
{
27+
parent::tearDown();
28+
$this->instance->__destruct();
29+
}
30+
31+
public function testGetInstance(): void
32+
{
33+
self::assertNotNull($this->instance);
34+
$anotherInstance = TmpDirRegistry::instance();
35+
self::assertEquals($this->instance, $anotherInstance);
36+
}
37+
38+
public function testDestructDirectory(): void
39+
{
40+
$dirname = 'testdirectory';
41+
$uniqueDirname = $this->instance->createDirInSystemTmp($dirname);
42+
$this->instance->__destruct();
43+
self::assertDirectoryNotExists($uniqueDirname);
44+
}
45+
46+
public function testDestructFile(): void
47+
{
48+
$filename = 'testfilename.txt';
49+
$dirname = 'testdirectory';
50+
$dir = $this->instance->createDirInSystemTmp($dirname);
51+
$uniqueFilename = $this->instance->createFileInSystemTmp($dir, $filename);
52+
$this->instance->__destruct();
53+
self::assertFileNotExists($uniqueFilename);
54+
}
55+
56+
public function testCreateDirInSystemTmpDirname(): void
57+
{
58+
$dirname = 'testdirectory';
59+
$uniqueDirname = $this->instance->createDirInSystemTmp($dirname);
60+
self::assertContains($dirname, $uniqueDirname);
61+
self::assertTrue(strlen($dirname) < strlen($uniqueDirname));
62+
}
63+
64+
public function testCreateDirInSystemTmp(): void
65+
{
66+
$dirname = 'testdirectory';
67+
$uniqueDirname = $this->instance->createDirInSystemTmp($dirname);
68+
self::assertFileExists($uniqueDirname);
69+
}
70+
71+
public function testCreateFileInSystemTmpFilename(): void
72+
{
73+
$dirname = $this->instance->createDirInSystemTmp('testdirectory');
74+
$filename = 'testfilename.txt';
75+
$uniqueFilename = $this->instance->createFileInSystemTmp($dirname, $filename);
76+
$uniqueFilenameInfo = pathinfo($uniqueFilename);
77+
$filenameInfo = pathinfo($filename);
78+
79+
self::assertContains($filenameInfo['filename'], $uniqueFilenameInfo['filename']);
80+
self::assertEquals($filenameInfo['extension'], $uniqueFilenameInfo['extension']);
81+
self::assertTrue(strlen($filename) < strlen($uniqueFilename));
82+
}
83+
84+
public function testCreateFileInSystemTmp(): void
85+
{
86+
$dirname = 'testdirectory';
87+
$filename = 'testfilename.txt';
88+
$instance = TmpDirRegistry::instance();
89+
$uniqueDirname = $instance->createDirInSystemTmp($dirname);
90+
$uniqueFilename = $instance->createFileInSystemTmp($uniqueDirname, $filename);
91+
self::assertFileExists($uniqueFilename);
92+
}
93+
}

tests/_bootstrap.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
require_once __DIR__ . '/../vendor/autoload.php';

0 commit comments

Comments
 (0)