Skip to content

Commit 721dc05

Browse files
authored
added Environment::bypassFinals() & FileMutator (#348)
1 parent b464a75 commit 721dc05

File tree

6 files changed

+257
-0
lines changed

6 files changed

+257
-0
lines changed

src/Framework/Environment.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,27 @@ public static function getTestAnnotations()
172172
}
173173

174174

175+
/**
176+
* Removes keyword final from source codes.
177+
* @return void
178+
*/
179+
public static function bypassFinals()
180+
{
181+
FileMutator::addMutator(function ($code) {
182+
if (strpos($code, 'final') !== false) {
183+
$tokens = token_get_all($code);
184+
$code = '';
185+
foreach ($tokens as $token) {
186+
$code .= is_array($token)
187+
? ($token[0] === T_FINAL ? '' : $token[1])
188+
: $token;
189+
}
190+
}
191+
return $code;
192+
});
193+
}
194+
195+
175196
/**
176197
* Loads data according to the file annotation or specified by Tester\Runner\TestHandler::initiateDataProvider()
177198
* @return array

src/Framework/FileMutator.php

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Tester.
5+
* Copyright (c) 2009 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
namespace Tester;
9+
10+
11+
/**
12+
* PHP file mutator.
13+
*/
14+
class FileMutator
15+
{
16+
const PROTOCOL = 'file';
17+
18+
/** @var resource|null */
19+
public $context;
20+
21+
/** @var resource|null */
22+
private $handle;
23+
24+
/** @var callable[] */
25+
private static $mutators = [];
26+
27+
28+
public static function addMutator(callable $mutator)
29+
{
30+
self::$mutators[] = $mutator;
31+
stream_wrapper_unregister(self::PROTOCOL);
32+
stream_wrapper_register(self::PROTOCOL, __CLASS__);
33+
}
34+
35+
36+
public function dir_closedir()
37+
{
38+
closedir($this->handle);
39+
}
40+
41+
42+
public function dir_opendir($path, $options)
43+
{
44+
$this->handle = $this->native('opendir', $path, $this->context);
45+
return (bool) $this->handle;
46+
}
47+
48+
49+
public function dir_readdir()
50+
{
51+
return readdir($this->handle);
52+
}
53+
54+
55+
public function dir_rewinddir()
56+
{
57+
return rewinddir($this->handle);
58+
}
59+
60+
61+
public function mkdir($path, $mode, $options)
62+
{
63+
return $this->native('mkdir', $mode, false, $this->context);
64+
}
65+
66+
67+
public function rename($pathFrom, $pathTo)
68+
{
69+
return $this->native('rename', $pathFrom, $pathTo, $this->context);
70+
}
71+
72+
73+
public function rmdir($path, $options)
74+
{
75+
return $this->native('rmdir', $this->context);
76+
}
77+
78+
79+
public function stream_cast($castAs)
80+
{
81+
return $this->handle;
82+
}
83+
84+
85+
public function stream_close()
86+
{
87+
fclose($this->handle);
88+
}
89+
90+
91+
public function stream_eof()
92+
{
93+
return feof($this->handle);
94+
}
95+
96+
97+
public function stream_flush()
98+
{
99+
return fflush($this->handle);
100+
}
101+
102+
103+
public function stream_lock($operation)
104+
{
105+
return flock($this->handle, $operation);
106+
}
107+
108+
109+
public function stream_metadata($path, $option, $value)
110+
{
111+
switch ($option) {
112+
case STREAM_META_TOUCH:
113+
return $this->native('touch', $path, $value[0], $value[1]);
114+
case STREAM_META_OWNER_NAME:
115+
case STREAM_META_OWNER:
116+
return $this->native('chown', $path, $value);
117+
case STREAM_META_GROUP_NAME:
118+
case STREAM_META_GROUP:
119+
return $this->native('chgrp', $path, $value);
120+
case STREAM_META_ACCESS:
121+
return $this->native('chmod', $path, $value);
122+
}
123+
}
124+
125+
126+
public function stream_open($path, $mode, $options, &$openedPath)
127+
{
128+
$usePath = (bool) ($options & STREAM_USE_PATH);
129+
if (pathinfo($path, PATHINFO_EXTENSION) === 'php') {
130+
$content = $this->native('file_get_contents', $path, $usePath, $this->context);
131+
if ($content === false) {
132+
return false;
133+
} else {
134+
foreach (self::$mutators as $mutator) {
135+
$content = call_user_func($mutator, $content);
136+
}
137+
$this->handle = tmpfile();
138+
$this->native('fwrite', $this->handle, $content);
139+
$this->native('fseek', $this->handle, 0);
140+
return true;
141+
}
142+
} else {
143+
$this->handle = $this->context
144+
? $this->native('fopen', $path, $mode, $usePath, $this->context)
145+
: $this->native('fopen', $path, $mode, $usePath);
146+
return (bool) $this->handle;
147+
}
148+
}
149+
150+
151+
public function stream_read($count)
152+
{
153+
return fread($this->handle, $count);
154+
}
155+
156+
157+
public function stream_seek($offset, $whence = SEEK_SET)
158+
{
159+
return fseek($this->handle, $offset, $whence);
160+
}
161+
162+
163+
public function stream_set_option($option, $arg1, $arg2)
164+
{
165+
}
166+
167+
168+
public function stream_stat()
169+
{
170+
return fstat($this->handle);
171+
}
172+
173+
174+
public function stream_tell()
175+
{
176+
return ftell($this->handle);
177+
}
178+
179+
180+
public function stream_truncate($newSize)
181+
{
182+
return ftruncate($this->handle, $newSize);
183+
}
184+
185+
186+
public function stream_write($data)
187+
{
188+
return fwrite($this->handle, $data);
189+
}
190+
191+
192+
public function unlink($path)
193+
{
194+
return $this->native('unlink', $path);
195+
}
196+
197+
198+
public function url_stat($path, $flags)
199+
{
200+
return $this->native('fstat', $path, $flags);
201+
}
202+
203+
204+
private function native($func)
205+
{
206+
stream_wrapper_restore(self::PROTOCOL);
207+
$res = call_user_func_array($func, array_slice(func_get_args(), 1));
208+
stream_wrapper_unregister(self::PROTOCOL);
209+
stream_wrapper_register(self::PROTOCOL, __CLASS__);
210+
return $res;
211+
}
212+
}

src/bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
require __DIR__ . '/Framework/FileMock.php';
1414
require __DIR__ . '/Framework/TestCase.php';
1515
require __DIR__ . '/Framework/DomQuery.php';
16+
require __DIR__ . '/Framework/FileMutator.php';
1617
require __DIR__ . '/CodeCoverage/Collector.php';
1718
require __DIR__ . '/Runner/Job.php';
1819

tests/Framework/FileMutator.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
use Tester\Assert;
4+
5+
require __DIR__ . '/../bootstrap.php';
6+
7+
8+
Tester\Environment::bypassFinals();
9+
10+
require __DIR__ . '/fixtures/final.class.php';
11+
12+
$rc = new ReflectionClass('FinalClass');
13+
Assert::false($rc->isFinal());
14+
Assert::false($rc->getMethod('finalMethod')->isFinal());
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
final class FinalClass
4+
{
5+
final function finalMethod()
6+
{
7+
}
8+
}

tests/coding-standard.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ parameters:
55
skip:
66
PHP_CodeSniffer\Standards\PSR1\Sniffs\Methods\CamelCapsMethodNameSniff:
77
- src/Framework/FileMock.php
8+
- src/Framework/FileMutator.php

0 commit comments

Comments
 (0)